# eng_network_multi_b30.py
# Version b30
# by jmg - j.gagen*AT*gold*DOT*ac*DOT*uk
# August 26th 2017

# Licence: http://creativecommons.org/licenses/by-nc-sa/3.0/
# Source code at: https://github.com/pha5echange/eng-tools

# Name changed to 'eng_network_multi'

# Plots network graph from edgelist 'ts_data\OMEGAYEAR\OMEGAYEAR_wuGraphData.txt'
# Adds dates from 'data\first_clusters.txt' as node attributes
# Adds artist numbers from 'ts_data\OMEGAYEAR_artistNums.txt' as node attributes
# Removes nodes where date-cluster information is not available
# Removes self-loop edges and zero-degree nodes if required
# Removes nodes based upon 'incepDate' if required - all nodes NEWER than Omega Year are removed
# Displays using parameters from 'config/config_nw.txt'
# Writes 'data\eng_network_wd_nodeList.txt' with nodes and degrees (k), '...edgeList.txt' with neighbours
# Writes analysis files to 'results\'
# Writes gexf files to 'gexf\', including 'gexf\YEAR.gexf' for use by 'nhm.py'
# Writes image to 'networks\'
# This version incorporates PageRank
# Incorporates Artist Time Slicing
# Added 'maxDeg' metric and isolated nodes counter
# Calculates mean in- and out-degree centrality for each network. Writes {dict}s to analysis files.
# Calculates graph flow hierarchy.

# Run AFTER 'eng_nodesets.py'

# Import packages
import os
import resource
import numpy as np
import networkx as nx
import community
from networkx.algorithms.approximation import clique
import matplotlib.pyplot as plt
from collections import OrderedDict
from datetime import datetime

versionNumber = ("b30")

# Initiate timing of run
runDate = datetime.now()
startTime = datetime.now()

# Create 'logs' subdirectory if necessary
if not os.path.exists("logs"):
    os.makedirs("logs")

# Create 'data' subdirectories if necessary
if not os.path.exists("data"):
	os.makedirs("data")

if not os.path.exists("data/node-edge-lists"):
	os.makedirs("data/node-edge-lists")

# Create 'gexf' subdirectories if necessary
if not os.path.exists("gexf"):
	os.makedirs("gexf")

if not os.path.exists("gexf/initial"):
	os.makedirs("gexf/initial")

if not os.path.exists("gexf/directed"):
	os.makedirs("gexf/directed")

if not os.path.exists("gexf/final"):
	os.makedirs("gexf/final")

# Create 'results' subdirectories if necessary
if not os.path.exists("results"):
	os.makedirs("results")

if not os.path.exists("results/analysis"):
	os.makedirs("results/analysis")

# Create 'networks' subdirectory if necessary
if not os.path.exists("networks"):
    os.makedirs("networks")

# Open file for writing log
logPath = os.path.join("logs", 'eng_network_multi_' + versionNumber + '_log.txt')
runLog = open(logPath, 'a')

# Begin
print ('\n' + "Multi-Network Thing | Version " + versionNumber + " | Starting...")
runLog.write ("==========================================================================" + '\n' + '\n')
runLog.write ("Multi-Network Thing | Version " + versionNumber + '\n' + '\n')

# Look for subfolders in `ts_data' and generate datelist from these
dateList = []
datePath = "ts_data/"
dateList = os.listdir(datePath)
dateSet = set(dateList)

# add 2015 to 'dateSet'
#dateSet.add(2015)

# Get user input
#print
#dateIP = int(input ("Enter a year to remove nodes that appear AFTER this date (or 0 to leave the network intact): "))
#selfLoopIP = int(input ("Enter 1 here to remove self-loop edges: "))
selfLoopIP = 1

#omegaYear = str(dateIP)
# Uncomment the line below to facilitate optional isolate removal
#isolatedIP = int(input ("Enter 1 here to remove isolated nodes: "))

# Comment out the line below if uncommenting the line above
isolatedIP = 0

# Remove genres AFTER the Omega Year
#dateInput = open(dateInputPath, 'r')

#for date in dateSet:

#	dateIP = int(date)
#	omegaYear = str(dateIP)

#	for line in dateInput:

#		genreInput, genreDate, newline = line.split(",")
#		intGenreDate = int(genreDate)

#		if intGenreDate <= dateIP:
#			genreName = str(omegaYear + '_' + genreInput).replace(" ", "")
#			dateDict[genreName] = genreDate

#sortDates = OrderedDict(sorted(dateDict.items()))
'''
for key, value in sortDates.iteritems():
	cleanDateList.append(int(value))

cleanDateSet = set(cleanDateList)

# Checking stuff
print('cleanDateList: ' + str(cleanDateList) + '\n')
print('cleanDateList elements: ' + str(len(cleanDateList)) + '\n')
print('cleanDateSet: ' + str(cleanDateSet) + '\n')
print ('cleanDateSet elements: ' + str(len(cleanDateSet)))
runLog.write('\n' + 'cleanDateList: ' + str(cleanDateList) + '\n')
runLog.write('\n' + 'cleanDateList elements: ' + str(len(cleanDateList)) + '\n')
runLog.write('\n' + 'cleanDateSet: ' + str(cleanDateSet) + '\n')
runLog.write('\n' + 'cleanDateSet elements: ' + str(len(cleanDateSet)) + '\n')
# End Checking stuff
'''

for date in dateList:

	dateIP = int(date)
	omegaYear = str(date)
	incepList = []
	incepDict = {}
	sortDates = OrderedDict()
	sortArtists = OrderedDict()

	# Generate a dict, list and set containing dates (from 'data\first_cluster.txt')
	dateInputPath = os.path.join("data", 'first_cluster.txt')
	dateInput = open(dateInputPath, 'r')

	for line in dateInput:

		genreInput, genreDate, newline = line.split(",")
		genreName = str(omegaYear + '_' + genreInput).replace(" ", "")
		incepDict[genreName] = genreDate
		incepList.append(int(genreDate))

	sortDates = OrderedDict(sorted(incepDict.items()))

	# Generate dictionary containing nodenames and artist numbers (from 'ts_data\OMEGAYEAR\_artistNums.txt')
	artistInputPath = os.path.join("ts_data", omegaYear, omegaYear + '_artistNums.txt')
	artistInput = open(artistInputPath, 'r').readlines()

	firstLine = artistInput.pop(0)

	artistDict = {}

	for line in artistInput:
		genreInput, artistNum, newline = line.split(",")
		genreName = str(genreInput).replace(" ", "")
		artistDict[genreName] = artistNum

	sortArtists = OrderedDict(sorted(artistDict.items()))

	# Checking stuff
	print ('\n' + "New Graph Omega Year: " + omegaYear)
	print ("DateList: " + str(dateList) + '\n')
	print ("DateList elements: " + str(len(dateList)) + '\n')
	print ("DateSet: " + str(dateSet) + '\n')
	print ("DateSet elements: " + str(len(dateSet)) + '\n')
	print ("SortDates: " + str(sortDates) + '\n')
	print ("SortDates elements: " + str(len(sortDates)) +'\n')
	print ("SortArtists: " + str(sortArtists) + '\n')
	print ("SortArtists elements: " + str(len(sortArtists)) +'\n')
	print

	runLog.write ('\n' + "New Graph Omega Year: " + omegaYear + '\n')
	runLog.write('\n' + "DateList: " + str(dateList) + '\n')
	runLog.write('\n' + "DateList elements: " + str(len(dateList)) + '\n')
	runLog.write('\n' + "DateSet: " + str(dateSet) + '\n')
	runLog.write('\n' + "DateSet elements: " + str(len(dateSet)) + '\n')
	runLog.write('\n' + "SortDates: " + str(sortDates) + '\n')
	runLog.write('\n' + "SortDates elements: " + str(len(sortDates)) +'\n')
	runLog.write('\n' + "SortArtists: " + str(sortArtists) + '\n')
	runLog.write('\n' + "SortArtists elements: " + str(len(sortArtists)) + '\n')
	# End Checking stuff

	# Open file to write list of nodes
	nodeListPath = os.path.join("ts_data", omegaYear, 'eng_network_multi_' + versionNumber + '_' + omegaYear + '_nodeList.txt')
	nodeListOP = open (nodeListPath, 'w')

	# Open file to write list of edges
	edgeListPath = os.path.join("ts_data", omegaYear, 'eng_network_multi_' + versionNumber + '_' + omegaYear + '_edgeList.txt')
	edgeListOP = open (edgeListPath, 'w')

	# Open file for writing initial gexf
	gexfPath = os.path.join("gexf/initial", 'eng_network_multi_' + versionNumber + '_' + omegaYear + '.gexf')
	gexfFile = open(gexfPath, 'w')

	# Open file for writing digraph gexf
	gexfDPath = os.path.join("gexf/directed", omegaYear + '.gexf')
	gexfDFile = open(gexfDPath, 'w')

	# Open file for writing final gexf
	gexfFinPath = os.path.join("gexf/final", 'eng_network_multi_' + versionNumber + '_' + omegaYear + '.gexf')
	gexfFinFile = open(gexfFinPath, 'w')

	# Open file for Page Rank data
	prPath = os.path.join("ts_data", omegaYear, omegaYear +'_multi_pagerank.txt')
	prFile = open(prPath, 'w')

	# Open file for analysis results
	anPath = os.path.join("results/analysis", 'eng_network_multi_' + versionNumber + '_' + omegaYear + '_analysis.txt')
	anFile = open(anPath, 'w')

	# Open file to write image
	nwImgPath = os.path.join("networks", 'eng_network_' + versionNumber + '_' + omegaYear + '_nw.eps')

	anFile.write ('\n' + "==========================================================================" + '\n' + '\n')
	anFile.write ("Multi-Network Thing | Version " + versionNumber + '\n' + '\n')

	# Read the edgelist and generate undirected graph
	print ('\n' + "Importing Weighted Edge List... ")
	inputPath = os.path.join("ts_data", omegaYear, omegaYear + '_wuGraph_data.txt')
	edgeList = open (inputPath, 'r')
	enGraph = nx.read_weighted_edgelist(edgeList, delimiter=',')

	# Calculate basic graph statistics
	print ('\n' + "Calculating various things... " + '\n')
	nodes = nx.number_of_nodes(enGraph)
	edges = nx.number_of_edges(enGraph)
	density = nx.density(enGraph)
	nodeList = nx.nodes(enGraph)
	nodeList.sort()
	selfLoopEdges = enGraph.number_of_selfloops()
	connections = edges - selfLoopEdges

	print ('Nodes: ' + str(nodes))
	print ('Edges: ' + str(edges))
	print ('Self-loops: ' + str(selfLoopEdges))
	print ('Connections (edges minus self-loops): ' + str(connections))
	print ('Density: ' + str(density))
	print ('Nodelist: ' + str(nodeList))
	print

	runLog.write ('\n' + 'Initial data: ' + '\n' + '\n')
	runLog.write ('Omega Year: ' + omegaYear + '\n')
	runLog.write ('Nodes: ' + str(nodes) + '\n')
	runLog.write ('Edges: ' + str(edges) + '\n')
	runLog.write ('Self-loops: ' + str(selfLoopEdges) + '\n')
	runLog.write ('Connections (edges minus self-loops): ' + str(connections) + '\n')
	runLog.write ('Density: ' + str(density) + '\n')
	runLog.write ('Nodelist: ' + str(nodeList) + '\n')

	# Apply artist numbers of nodes as attributes
	print ('Applying total artist number attribute to nodes...' + '\n')
	runLog.write ('\n' + 'Applying total artist number attribute to nodes...' + '\n' + '\n')
	for i in nodeList:
		enGraph.node[i]['totalArtists'] = sortArtists[i]

	# Checking stuff
	allNodes = enGraph.nodes(data = True)
	print ("All Nodes with artist numbers: " + str(allNodes) + '\n')
	runLog.write("All Nodes with artist numbers: " + str(allNodes) + '\n' + '\n')
	# End checking stuff

	# Apply dates of nodes as attributes
	print ('Applying inception date attribute to nodes...' + '\n')
	runLog.write ('\n' + 'Applying inception date attribute to nodes...' + '\n' + '\n')
	for i in nodeList:
		try:
			enGraph.node[i]['incepDate'] = sortDates[i]
		except:
			pass

	# Checking stuff
	allNodes = enGraph.nodes(data = True)
	print ("All Nodes with inception dates: " + str(allNodes) + '\n')
	runLog.write("All Nodes with inception dates: " + str(allNodes) + '\n' + '\n')
	# End checking stuff

	# Discard nodes without first-cluster dates
	noDateNode = 0
	print ('Checking genre inception dates, and removing if none...' + '\n')
	runLog.write ('Checking genre inception dates, and removing if none...' + '\n')
	for i in nodeList:
		if not i in sortDates.keys():
			enGraph.remove_node(i)
			print ('Removed node ' + str(i))
			runLog.write('Removed node ' + str(i) + '\n')
			noDateNode += 1

	if noDateNode == 1:
		print ('\n' + 'Removed ' + str(noDateNode) + ' node due to no inception date.')
		runLog.write ('\n' + 'Removed ' + str(noDateNode) + ' node due to no inception date.' + '\n')

	if noDateNode > 1:
		print ('\n' + 'Removed ' + str(noDateNode) + ' nodes due to no inception dates.')
		runLog.write ('\n' + 'Removed ' + str(noDateNode) + ' nodes due to no inception dates.' + '\n')

	# Recalculate basic graph statistics
	print ('\n' + 'Recalculating various things...' + '\n')
	nodes = nx.number_of_nodes(enGraph)
	edges = nx.number_of_edges(enGraph)
	density = nx.density(enGraph)
	nodeList = nx.nodes(enGraph)
	nodeList.sort()
	selfLoopEdges = enGraph.number_of_selfloops()
	connections = edges - selfLoopEdges

	print ('Stage 1 data: ' + '\n')
	print ('Nodes: ' + str(nodes))
	print ('Edges: ' + str(edges))
	print ('Self-loops: ' + str(selfLoopEdges))
	print ('Connections (edges minus self-loops): ' + str(connections))
	print ('Density: ' + str(density))
	print

	runLog.write ('\n' + 'Stage 1 data: ' + '\n' + '\n')
	runLog.write ('Nodes: ' + str(nodes) + '\n')
	runLog.write ('Edges: ' + str(edges) + '\n')
	runLog.write ('Self-loops: ' + str(selfLoopEdges) + '\n')
	runLog.write ('Connections (edges minus self-loops): ' + str(connections) + '\n')
	runLog.write ('Density: ' + str(density) + '\n')

	# Remove nodes where 'incepDate' attribute is NEWER than omega year ('dateIP')
	if dateIP != 0:
		print ("Removing nodes based upon omega year..." + '\n')
		runLog.write('\n' + 'Removing nodes based upon omega year...' + '\n')
		for i in nodeList:
			incepDate = nx.get_node_attributes (enGraph, 'incepDate')
			intIncep = int(incepDate[i])

			if intIncep > dateIP:
				enGraph.remove_node(i)
				print ('Removed newer node ' + str(i) + ' based upon omega year.')

	else:
		print ('No nodes removed based upon omega year.')
		runLog.write ('\n' + 'No nodes removed based upon omega year.' + '\n')

	# Recalculate basic graph statistics
	print ('\n' + 'Recalculating various things...' + '\n')
	nodes = nx.number_of_nodes(enGraph)
	edges = nx.number_of_edges(enGraph)
	density = nx.density(enGraph)
	nodeList = nx.nodes(enGraph)
	nodeList.sort()
	selfLoopEdges = enGraph.number_of_selfloops()
	connections = edges - selfLoopEdges

	print ('Nodes: ' + str(nodes))
	print ('Edges: ' + str(edges))
	print ('Self-loops: ' + str(selfLoopEdges))
	print ('Connections (edges minus self-loops): ' + str(connections))
	print ('Density: ' + str(density))
	print

	runLog.write ('\n' + 'Stage 2 data: ' + '\n' + '\n')
	runLog.write ('Nodes: ' + str(nodes) + '\n')
	runLog.write ('Edges: ' + str(edges) + '\n')
	runLog.write ('Self-loops: ' + str(selfLoopEdges) + '\n')
	runLog.write ('Connections (edges minus self-loops): ' + str(connections) + '\n')
	runLog.write ('Density: ' + str(density) + '\n')

	# Remove self-loops if required
	selfLoopCount = 0
	if selfLoopIP == 1:
		print ('\n' + 'Checking for and removing self-loops...' + '\n')
		runLog.write('\n' + 'Checking for and removing self-loops...' + '\n')
		for u,v,data in enGraph.edges(data=True):
			if u == v:
				enGraph.remove_edge(u,v)
				print ('removed self-loop ' + str(u))
				selfLoopCount += 1

		if selfLoopCount == 1:
			print ('\n' + 'Removed ' + str(selfLoopCount) + ' self-loop edge.')
			runLog.write ('\n' + 'Removed ' + str(selfLoopCount) + ' self-loop edge.' + '\n')

		if selfLoopCount > 1:
			print ('\n' + 'Removed ' + str(selfLoopCount) + ' self-loop edges.')
			runLog.write ('\n' + 'Removed ' + str(selfLoopCount) + ' self-loop edges.' + '\n')
	else:
		print ('Self-loops intact.' + '\n')
		runLog.write('\n' + 'Self-loops intact.' + '\n')

	# Remove zero degree nodes if required
	isolateCount = 0
	if isolatedIP == 1:
		print ('\n' + 'Checking for and removing isolated (zero degree) nodes...' +'\n')
		runLog.write('\n' + 'Checking for and removing isolated (zero degree) nodes...' +'\n' + '\n')
		for i in nodeList:
			if nx.is_isolate(enGraph,i):
				enGraph.remove_node(i)
				print ('Removed isolated node ' + str(i))
				runLog.write('Removed isolated node ' + str(i) + '\n')
				isolateCount += 1

		if isolateCount == 1:
			print ('\n' + "Removed " + str(isolateCount) + " isolated node. " + '\n')
			runLog.write ('\n' + "Removed " + str(isolateCount) + " isolated node. " + '\n')

		if isolateCount > 1:
			print ('\n' + "Removed " + str(isolateCount) + " isolated nodes. " + '\n')
			runLog.write ('\n' + "Removed " + str(isolateCount) + " isolated nodes. " + '\n')

	else:

		for i in nodeList:
			if nx.is_isolate(enGraph,i):
				isolateCount += 1

		if isolateCount == 1:
			print ('\n' + str(isolateCount) + ' isolated node intact.')
			runLog.write('\n' + str(isolateCount) + ' isolated node intact.' + '\n')

		if isolateCount > 1:
			print ('\n' + str(isolateCount) + ' isolated nodes intact.')
			runLog.write('\n' + str(isolateCount) + ' isolated nodes intact.' + '\n')

	# Recalculate basic graph statistics
	print ('Recalculating various things...' + '\n')
	nodes = nx.number_of_nodes(enGraph)
	edges = nx.number_of_edges(enGraph)
	density = nx.density(enGraph)
	nodeList = nx.nodes(enGraph)
	nodeList.sort()
	selfLoopEdges = enGraph.number_of_selfloops()

	print ('Nodes: ' + str(nodes))
	print ('Isolated nodes:' + str(isolateCount))
	print ('Edges: ' + str(edges))
	print ('Self-loops: ' + str(selfLoopEdges))
	print ('Density: ' + str(density))
	print

	runLog.write ('\n' + 'Stage 3 data: ' + '\n' + '\n')
	runLog.write ('Nodes: ' + str(nodes) + '\n')
	runLog.write ('Isolated nodes:' + str(isolateCount) + '\n')
	runLog.write ('Edges: ' + str(edges) + '\n')
	runLog.write ('Self-loops: ' + str(selfLoopEdges) + '\n')
	runLog.write ('Density: ' + str(density) + '\n')

	# Clean edge-labels
	print ('Cleaning edge labels...')
	labels = {}
	for u,v,data in enGraph.edges(data=True):
		labels[(u,v)] = int(data['weight'])

	# Write file with nodes and degree,for reference
	print ('\n' + 'Writing node list with degree and neighbours...' + '\n')
	for i in nodeList:
		nodeDegree = enGraph.degree(i)
		neighboursList = list(nx.all_neighbors(enGraph, i))
		nodeListOP.write(str(i) + ',' + str(nodeDegree) + ',' + str(neighboursList) +'\n')
		print ("Node " + str(i) + " degree: " + str(nodeDegree))
		print ("Neighbours: " + str(neighboursList))

	nodeListOP.close()

	# Calculate new graph-wide total artists value
	# Use artist-total node attributes
	totalArtists = 0
	totalEdgeWeight = 0
	edgeList = enGraph.edges(data=True)

	for i in nodeList:
		nodeArtists = int(sortArtists[i])
		totalArtists += nodeArtists

	for i in edgeList:
		nodesStr = str(i).replace("u'","").replace("'","").replace("(","").replace(")","").replace(" ","")
		nodeU, nodeV, edgeData = nodesStr.split(",")
		edgeWeight = float(str(edgeData).replace("{weight:","").replace("}",""))
		totalEdgeWeight += edgeWeight

	# Analysis
	print ('\n' + 'Analysing undirected graph...' + '\n')
	try:
		print ('Average clustering coefficient...' + '\n')
		avClustering = nx.average_clustering(enGraph)
	except:
		pass
	print ('Connected components...' + '\n')
	connectComp = [len(c) for c in sorted(nx.connected_components(enGraph), key=len, reverse=True)]
	print ('Find cliques...' + '\n')
	cl = nx.find_cliques(enGraph)
	cl = sorted(list(cl), key = len, reverse = True)
	print ('Number of cliques: ' + str(len(cl)) + '\n')
	cl_sizes = [len(c) for c in cl]
	print ('Size of cliques: ' + str(cl_sizes))

	print ('\n' + 'Undirected graph data: ' + '\n')
	print ('Nodes: ' + str(nodes))
	print ('Edges: ' + str(edges))
	print ('Self-loops: ' + str(selfLoopEdges))
	print ('Density: ' + str(density))
	print ('Average Clustering Coefficient: ' + str(avClustering))
	print ('Number of cliques: ' + str(len(cl)))
	print ('Connected Components: ' + str(connectComp))
	print
	print (str(nx.info(enGraph)))
	print

	runLog.write ('\n' + 'Undirected graph data: ' + '\n' + '\n')
	runLog.write ('Nodes: ' + str(nodes) + '\n')
	runLog.write ('Edges: ' + str(edges) + '\n')
	runLog.write ('Self-loops: ' + str(selfLoopEdges) + '\n')
	runLog.write ('Density: ' + str(density) + '\n')
	runLog.write ('Average Clustering Coefficient: ' + str(avClustering) + '\n')
	runLog.write ('Number of cliques: ' + str(len(cl)) + '\n')
	runLog.write ('Connected Components: ' + str(connectComp) + '\n')
	runLog.write ('\n' + str(nx.info(enGraph)) + '\n')

	# Write undirected gexf file for use in Gephi
	print ("Writing undirected gexf file... " + '\n')
	runLog.write('\n' + "Writing undirected gexf file... " + '\n')
	nx.write_gexf(enGraph, gexfFile)
	gexfFile.close()

	# Direct graph and write gexf of this
	print ("Directing graph... " + '\n')
	runLog.write('\n' + "Directing graph..." + '\n')
	diEnGraph = nx.DiGraph()
	diEnGraph.add_nodes_from(enGraph.nodes(data=True))
	diEnGraph.add_edges_from(enGraph.edges(data=True))

	edgeList = diEnGraph.edges(data=True)
	removedNodes = 0
	for i in edgeList:
		# clean up fraking unicode string
		nodesStr = str(i).replace("u'","").replace("'","").replace("(","").replace(")","").replace(" ","")

		# display / write a list of edges
		print(nodesStr)
		edgeListOP.write(nodesStr + '\n')

		# implement calculation for edge-direction
		nodeU, nodeV, edgeData = nodesStr.split(",")
		edgeWeight = float(str(edgeData).replace("{weight:","").replace("}",""))
		nodeUdate = int(sortDates[nodeU])
		print(nodeUdate)
		nodeVdate = int(sortDates[nodeV])
		print (nodeVdate)

		if nodeUdate > nodeVdate:
			# direct from v to u
			print("Directing edge from V to U" + '\n')
			# remove the edge
			diEnGraph.remove_edge(nodeU,nodeV)
			# add a directed edge
			diEnGraph.add_edge(nodeV, nodeU, weight=edgeWeight)

		elif nodeVdate > nodeUdate:
			# direct from u to v
			print("Direct from U to V" + '\n')
			# remove the edge
			diEnGraph.remove_edge(nodeU,nodeV)
			# add a directed edge
			diEnGraph.add_edge(nodeU, nodeV,  weight=edgeWeight)

		elif nodeUdate == nodeVdate:
			# remove the edge
			diEnGraph.remove_edge(nodeU,nodeV)
			# write to a list of removed edges
			removedNodes += 1
			print("Removed edge between nodes " + str(nodeU) + " and " + str(nodeV) + " due to identical node inception dates. " + '\n')
			edgeListOP.write("Removed edge between nodes " + str(nodeU) + " and " + str(nodeV) + " due to identical node inception dates. " + '\n')

	print (str(removedNodes) + " edges removed due to identical node inception dates. " + '\n')
	runLog.write ('\n' + str(removedNodes) + " edges removed due to identical node inception dates. " + '\n')
	edgeListOP.close()

	print ("Writing directed gexf file... " + '\n')
	runLog.write('\n' + "Writing directed gexf file... " + '\n')
	nx.write_gexf(diEnGraph, gexfDFile)
	gexfDFile.close()

	# Recheck zero degree nodes
	print ('Rechecking isolated (zero degree) nodes...' +'\n')
	runLog.write('\n' + 'Rechecking isolated (zero degree) nodes...' +'\n' + '\n')

	diNodeList = nx.nodes(diEnGraph)
	diNodeList.sort()

	isolateCount = 0
	if isolatedIP == 1:

		for i in diNodeList:
			if nx.is_isolate(diEnGraph,i):
				diEnGraph.remove_node(i)
				print ('Removed isolated node ' + str(i))
				runLog.write('Removed isolated node ' + str(i) + '\n')
				isolateCount += 1

		if isolateCount == 1:
			print ("Removed " + str(isolateCount) + " isolated node. " + '\n')
			runLog.write ('\n' + "Removed " + str(isolateCount) + " isolated node. " + '\n')

		if isolateCount > 1:
			print ("Removed " + str(isolateCount) + " isolated nodes. " + '\n')
			runLog.write ('\n' + "Removed " + str(isolateCount) + " isolated nodes. " + '\n')

	else:

		for i in diNodeList:
			if nx.is_isolate(diEnGraph,i):
				isolateCount += 1

		if isolateCount == 1:
			print (str(isolateCount) + ' isolated node intact.' +'\n')
			runLog.write('\n' + str(isolateCount) + ' isolated node intact.' + '\n')

		if isolateCount > 1:
			print (str(isolateCount) + ' isolated nodes intact.' + '\n')
			runLog.write('\n' + str(isolateCount) + ' isolated nodes intact.' + '\n')

	# Plot and display graph
	# Graph plotting parameters - moved to config file 'config_nw.txt'
	print ('Reading layout config file...' + '\n')

	# Open and read 'config_nw.txt'
	nwConfigPath = os.path.join ("config", 'config_nw.txt')
	nwConfig = open(nwConfigPath, 'r').readlines()

	# Remove the first line
	firstLine = nwConfig.pop(0)

	for line in nwConfig:
		n_size, n_alpha, node_colour, n_text_size, text_font, e_thickness, e_alpha, edge_colour, l_pos, e_text_size, edge_label_colour = line.split(",")

	node_size = int(n_size)
	node_alpha = float(n_alpha)
	node_text_size = int(n_text_size)
	edge_thickness = int(e_thickness)
	edge_alpha = float(e_alpha)
	label_pos = float(l_pos)
	edge_text_size = int(e_text_size)

	print ('Laying out graph...' + '\n')

	#nx.draw(enGraph)
	graph_pos = nx.spring_layout(diEnGraph)
	nx.draw_networkx_nodes(diEnGraph, graph_pos, node_size = node_size, alpha = node_alpha, node_color=node_colour)
	nx.draw_networkx_edges(diEnGraph, graph_pos, width = edge_thickness, alpha = edge_alpha, color = edge_colour)
	#nx.draw_networkx_labels(diEnGraph, graph_pos, font_size = node_text_size, font_family = text_font)
	#nx.draw_networkx_edge_labels(diEnGraph, graph_pos, edge_labels = labels, label_pos = label_pos, font_color = edge_label_colour, font_size = edge_text_size, font_family = text_font)

	# write image file
	print ('Writing image file...' + '\n')
	plt.savefig(nwImgPath, format = 'eps', bbox_inches='tight')

	# display graph
	#print ('Displaying graph...' + '\n')
	#plt.show()

	# Clear plot
	plt.clf()

	# Recalculate basic graph statistics
	nodes = nx.number_of_nodes(diEnGraph)
	edges = nx.number_of_edges(diEnGraph)
	density = nx.density(diEnGraph)
	isDag = nx.is_directed_acyclic_graph(diEnGraph)

	# Calculate degree_centrality
	try:
		inDegCent = nx.in_degree_centrality(diEnGraph)
		outDegCent = nx.out_degree_centrality(diEnGraph)
	except:
		inDegCent = {}
		outDegCent = {}

	inDegCount = 0
	inDegSum = 0
	inDegMean = 0
	for key in inDegCent:
		inDegCount += 1
		inDegSum += inDegCent[key]

	try:
		inDegMean = inDegSum/inDegCount
	except:
		inDegMean = 0

	outDegCount = 0
	outDegSum = 0
	outDegMean = 0
	for key in outDegCent:
		outDegCount += 1
		outDegSum += outDegCent[key]

	try:
		outDegMean = outDegSum/outDegCount
	except:
		outDegMean = 0

	# Calculate flow hierarchy
	try:
		graphFlow = nx.flow_hierarchy(diEnGraph)
	except:
		graphFlow = 0

	print ('Final Directed Graph Information' + '\n')
	print ('Omega Year: ' + omegaYear)
	print ('Nodes: ' + str(nodes))
	print ('Edges: ' + str(edges))
	print ('Density: ' + str(density))
	print ('Is DAG? ' + str(isDag))
	print
	print ("Mean In-degree Centrality: " + str(inDegMean))
	print ("Mean Out-degree Centrality: " + str(outDegMean))
	print ("Flow Hierarchy: " + str(graphFlow))
	print
	print (str(nx.info(diEnGraph)))

	anFile.write ('Nodes: ' + str(nodes) + '\n')
	anFile.write ('Edges: ' + str(edges) + '\n')
	anFile.write ('Density: ' + str(density) + '\n')
	anFile.write ('Is DAG? ' + str(isDag) + '\n')
	anFile.write ('\n' + "In-degree Centrality: " + str(inDegCent) + '\n')
	anFile.write ('\n' + "Out-degree Centrality: " + str(outDegCent) + '\n' + '\n')
	anFile.write ("Mean In-degree Centrality: " + str(inDegMean) + '\n')
	anFile.write ("Mean Out-degree Centrality: " + str(outDegMean) + '\n')
	anFile.write ("Flow Hierarchy: " + str(graphFlow) + '\n')
	anFile.write ('\n' + str(nx.info(diEnGraph)) + '\n')

	runLog.write ('\n' + 'Final Directed Graph Information' + '\n' + '\n')
	runLog.write ('Omega Year: ' + omegaYear + '\n')
	runLog.write ('Nodes: ' + str(nodes) + '\n')
	runLog.write ('Edges: ' + str(edges) + '\n')
	runLog.write ('Density: ' + str(density) + '\n')
	runLog.write ('Is DAG? ' + str(isDag) + '\n')
	runLog.write ("Mean In-degree Centrality: " + str(inDegMean) + '\n')
	runLog.write ("Mean Out-degree Centrality: " + str(outDegMean) + '\n')
	runLog.write ("Flow Hierarchy: " + str(graphFlow) + '\n')
	runLog.write ('\n' + str(nx.info(diEnGraph)) + '\n')

	prFile.write(str(nx.pagerank(diEnGraph)))
	prFile.close()

	# Render undirected version of diEnGraph to facilitate final analysis of graph characteristics
	print('\n' + "Rendering undirected verison to facilitate final analysis of graph characteristics... ")
	runLog.write('\n' + "Rendering undirected verison to facilitate final analysis of graph characteristics... " + '\n')
	newEnGraph = diEnGraph.to_undirected()

	# Analysis
	print ('\n' + 'Analysing final undirected graph...' + '\n')
	print ('Maximal degree... ' + '\n')
	degreeSeq = sorted(nx.degree(newEnGraph).values(),reverse=True)
	maxDeg = max(degreeSeq)
	print ('Maximal degree: ' + str(maxDeg) + '\n')
	anFile.write ('Maximal degree: ' + str(maxDeg) + '\n')
	print ('Average clustering coefficient...' + '\n')
	avClustering = nx.average_clustering(newEnGraph)
	print ('Connected components...' + '\n')
	connectComp = [len(c) for c in sorted(nx.connected_components(newEnGraph), key=len, reverse=True)]
	print ('Find cliques...' + '\n')
	cl = nx.find_cliques(newEnGraph)
	cl = sorted(list(cl), key = len, reverse = True)
	print ('Number of cliques: ' + str(len(cl)) + '\n')
	anFile.write ('Number of cliques: ' + str(len(cl)) + '\n')
	cl_sizes = [len(c) for c in cl]
	print ('Size of cliques: ' + str(cl_sizes))
	anFile.write ('Size of cliques: ' + str(cl_sizes) + '\n' + '\n')

	# Write undirected gexf file for use in Gephi
	print ('\n' + 'Writing final undirected gexf file...' + '\n')
	nx.write_gexf(newEnGraph, gexfFinFile)
	gexfFinFile.close()

	anFile.write ('\n' + 'Final Undirected Graph Information' + '\n' + '\n')
	anFile.write ('Date of run: {}'.format(runDate) + '\n')
	anFile.write ('Omega Year: ' + omegaYear + '\n')
	anFile.write ("Total artists in all genres: " + str(totalArtists) + '\n')
	anFile.write ("Total edge-weighting: " + str(int(totalEdgeWeight)) + '\n')
	anFile.write ('Nodes: ' + str(nodes) + '\n')
	anFile.write ('Isolated nodes: ' + str(isolateCount) + '\n')
	anFile.write ('Edges: ' + str(edges) + '\n')
	anFile.write ('Density: ' + str(density) + '\n')
	anFile.write ('Maximal degree: ' + str(maxDeg) + '\n')
	anFile.write ('Average Clustering Coefficient: ' + str(avClustering) + '\n')
	anFile.write ('Number of cliques: ' + str(len(cl)) + '\n')
	anFile.write ('Connected Components: ' + str(connectComp) + '\n')
	anFile.write ('\n' + str(nx.info(newEnGraph)))
	anFile.close()

	print ('Final Undirected Graph Information' + '\n')
	print ('Omega Year: ' + omegaYear)
	#print ("Total artists in all genres: " + str(totalArtists))
	#print ("Total edge-weighting: " + str(int(totalEdgeWeight)))
	print ('Nodes: ' + str(nodes))
	print ('Isolated nodes: ' + str(isolateCount))
	print ('Edges: ' + str(edges))
	print ('Self-loops: ' + str(selfLoopEdges))
	print ('Density: ' + str(density))
	print ('Maximal degree: ' + str(maxDeg))
	print ('Average Clustering Coefficient: ' + str(avClustering))
	print ('Number of cliques: ' + str(len(cl)))
	print ('Connected Components: ' + str(connectComp))
	print
	print (str(nx.info(newEnGraph)))
	print

	runLog.write ('\n' + 'Final Undirected Graph Information' + '\n' + '\n')
	runLog.write ('Omega Year: ' + omegaYear + '\n')
	#runLog.write ("Total artists in all genres: " + str(totalArtists) + '\n')
	#runLog.write ("Total edge-weighting: " + str(int(totalEdgeWeight)) + '\n')
	runLog.write ('Nodes: ' + str(nodes) + '\n')
	runLog.write ('Isolated nodes:' + str(isolateCount) + '\n')
	runLog.write ('Edges: ' + str(edges) + '\n')
	runLog.write ('Self-loops: ' + str(selfLoopEdges) + '\n')
	runLog.write ('Density: ' + str(density) + '\n')
	runLog.write ('Maximal degree: ' + str(maxDeg) + '\n')
	runLog.write ('Average Clustering Coefficient: ' + str(avClustering) + '\n')
	runLog.write ('Number of cliques: ' + str(len(cl)) + '\n')
	runLog.write ('Connected Components: ' + str(connectComp) + '\n' + '\n')
	runLog.write (str(nx.info(newEnGraph)) + '\n')

	# Clear Graph
	enGraph.clear()

# End timing of run
endTime = datetime.now()

print
print ('Date of run: {}'.format(runDate))
print ('Duration of run : {}'.format(endTime - startTime))
runLog.write ('\n' + '\n' + 'Date of run: {}'.format(runDate) + '\n')
runLog.write('Duration of run : {}'.format(endTime - startTime) + '\n')
runLog.close()


from bs4 import BeautifulSoup
from bs4 import element
import requests
import asyncio
import time
import aiohttp

count = 0
async def query(link):
	'''функция для обработки запроса.
		Принимает url и возвращает объект BeautifulSoup
		для дальнейшей обработки'''
	headers = {
			'User-Agent':'Mozilla/5.0 (X11; Linux x86_64; rv:12.0) \
				Gecko/20100101 Firefox/12.0'}
	async with aiohttp.ClientSession() as session:
		async with session.get(link) as response:
			response = await response.read()
	soup = BeautifulSoup(response.decode('utf-8'), 'html.parser')
	return soup

def link_to_new_page(base_url, next_page_obj):
	'''функция для определения наличия дополнительных страниц.
		Принимает объект Node, в котором ищется ссылка на
		следующую страницу с объявлениеми.
		Возвращает None или ссылку на страницу.'''
	if next_page_obj:
		print(1)
		if next_page_obj.name == 'a':
			new_page = base_url + next_page_obj.get('href')
		else:
			if next_page_obj.a:
				new_page = base_url + next_page_obj.a.get('href')
			else:
				new_page = None
	else:
		new_page = None
	return new_page

def text_formatting(tag):
	'''функция для форматирования текста.
		На вход получаем объект Node, из которого извлекается текст.
		Возвращает строку, соотв. тексту Node'''
	text = ''
	for i in tag.children:
		if isinstance(i, element.NavigableString):
			text += i + ' '
		else:
			child = i.children
			if child:
				for j in child:
					if isinstance(j, element.NavigableString):
						text += j + ' '
					else:
						if j.name in ['b', 'i', 'span']:
							text += j.get_text() + ' '
						else:
							text += j.get_text() + '
'
				text += '
'
			else:
				text += i.get_text() + '
'

	return text

async def rabota_ua():
	'''фукнция для извлечения данных из https://rabota.ua'''
	base_url = 'https://rabota.ua'
	new_page = base_url +\
	"/jobsearch/vacancy_list?keyWords=python&scheduleId=3&parentId=1"
	out = ''
	global count
	while new_page:
		#заходим на страницу с объявлениями
		soup = await query(new_page)
		divs = soup.findAll('div', attrs={'class':'fd-f1'})
		#проверяем существует ли следующая страница
		next_page = soup.find('dd', attrs={'class':'nextbtn'})
		new_page = link_to_new_page(base_url, next_page)
		for div in divs:
			if div.h3:
				#обрабатываем каждое объявление
				title = div.h3.string.strip() + '\n'
				a = div.h3.a.get('href').strip()
				href = base_url + a
				out += '
{1} -\
						{2}
'.format(href, base_url, title)
				#заходим на страницу объявления
				soup = await query(href)
				div = soup.find('div',
				attrs={'class':'f-vacancy-description-inner-content'})

				text = text_formatting(div)
				out += '
{}
'.format(text)
				count += 1
	return out

async def work_ua():
	'''фукнция для извлечения данных из https://www.work.ua'''
	base_url = 'https://www.work.ua'
	new_page = base_url +\
	"/jobs-python/?advs=1&sel_zan=76"
	out = ''
	global count
	while new_page:
		#заходим на страницу с объявлениями
		soup = await query(new_page)
		divs = soup.findAll('div', attrs={
								'class':'card card-hover card-visited\
								 wordwrap job-link card-logotype'})
		divs += soup.findAll('div', attrs={
								'class':'card card-hover card-visited\
									 wordwrap job-link'})
		ul = soup.find('ul', attrs={'class':"pagination hidden-xs"})
		#проверяем существует ли следующая страница
		if ul:
			next_page = ul.findAll('li')[-1]
			new_page = link_to_new_page(base_url, next_page)
		else:
			new_page = None
		for d in divs:
			#обрабатываем каждое объявление
			title = d.find('h2').a.string.strip()
			a = d.find('h2').a.get('href')
			href = base_url + a
			out += '
{1} - \
					{2}
'.format(href, base_url, title)
			#заходим на страницу объявления
			soup = await query(href)
			div = soup.find('div', attrs={'class':"overflow wordwrap"})
			text = text_formatting(div)
			out += '
{}\n
'.format(text)
			count += 1

	return out

async def hh_ua():
	'''фукнция для извлечения данных из https://hh.ua'''
	base_url = 'https://hh.ua'
	new_page = base_url +\
	"/search/vacancy?order_by=publication_time&schedule=remote&\
	enable_snippets=true&area=5&text=Python&clusters=true&\
	search_period=7&currency_code=UAH&experience=between1And3&\
	from=cluster_experience"
	out = ''
	global count
	while new_page:
		#заходим на страницу с объявлениями
		soup = await query(new_page)
		divs = soup.findAll('div',
					attrs={'class':'vacancy-serp-item__info'})
		#проверяем существует ли следующая страница
		next_page = soup.find('a', attrs={
									'class':"bloko-button \
										HH-Pager-Controls-Next \
										HH-Pager-Control"})
		new_page = link_to_new_page(base_url, next_page)
		for d in divs:
			t = d.find('div',
					attrs={'class':'vacancy-serp-item__title'})
			if t:
				#обрабатываем каждое объявление
				title = t.a.string.strip()
				href = t.a.get('href')
				out += '
{1} - \
						{2}
'.format(href, base_url, title)
				#заходим на страницу объявления
				soup = await query(href)
				description = soup.find('div',
								attrs={'class':"g-user-content"})
				if not description:
					description = soup.find('div',
						attrs={'class':'vacancy-branded-user-content'})
				requirements = soup.find('div',
						attrs={'class':"bloko-gap bloko-gap_bottom"})
				text = text_formatting(description)
				out += '
{0}\n{1}
'.format(
										requirements.get_text(), text)
				count += 1
	return out

async def main(loop):
	'''функция для обработки 3 сайтов и составления результирующей
	html-страницы'''
	#обрабатываем сайты
	completed, pending = await asyncio.wait([work_ua(),
											rabota_ua(),
											hh_ua()])
	#создаем результирующую html-страницу
	html = '<title>Вакансии</title><meta charset="utf-8">\
	<link rel="stylesheet" type="text/css" href="vacancy.css"\
	'
	html += '
\
				Найдено объявлений: {}
'.format(count)
	#наполняем страницу содержимым
	for item in completed:
		html += item.result()
	html += ''
	#записываем полученную страницу в файл
	with open('vacancy.html', 'w') as f:
		f.write(html)

if __name__ == '__main__':
	t = time.time()
	loop = asyncio.get_event_loop()
	loop.run_until_complete(main(loop))
	loop.close()
	print(time.time()-t)
	print('Всего объявлений:', count)


from pylab import *

import getpass
users={"candi": "C:\\Users\\candi\\Documents\\GitHub\\Lab3.2\\",
"silvanamorreale":"C:\\Users\\silvanamorreale\\Documents\\GitHub\\Lab3.2\\" ,
"Studenti": "C:\\Users\\Studenti\\Desktop\\Lab3\\",
"User":"C:\\Users\\User\\Documents\\GitHub\\Lab3.2\\"
}
try:
    user=getpass.getuser()
    path=users[user]
    print("buongiorno ", user, "!!!")
except:
    raise Error("unknown user, please specify it and the path in the file Esercitazione*.py")

sys.path = sys.path + [path]
dir= path + "Esercitazione11\\"

from BuzzLightyear import *
from uncertainties import *
import uncertainties
###########################################################################

from Oscillografo import *

AB = OscilloscopeData(dir+"data\\AB.csv")
andAO = OscilloscopeData(dir+"data\\andAO.csv")
orAO = OscilloscopeData(dir+"data\\orAO.csv")
xorAO = OscilloscopeData(dir+"data\\xorAO.csv")

figure(1)
fig,ax = subplots(1)

plot(AB.T1, AB.CH1, ",--", label="input1")
plot(AB.T2, AB.CH2, ",--", label="input2")
plot(andAO.T2, andAO.CH2, ",-", label="AND")

ax.set_yticklabels([])
ax.set_xticklabels([])
legend(loc=6)
grid()
savefig(dir + "grafici\\ANDard.pdf")

figure(2)
fig,ax = subplots(1)
grid()
plot(AB.T1, AB.CH1, ",--", label="input1")
plot(AB.T2, AB.CH2, ",--", label="input2")
plot(orAO.T2, orAO.CH2, ",-", label="OR")

ax.set_yticklabels([])
ax.set_xticklabels([])
legend(loc=6)
grid()
savefig(dir + "grafici\\ORard.pdf")

figure(3)
fig,ax = subplots(1)

plot(AB.T1, AB.CH1, ",--", label="input1")
plot(AB.T2, AB.CH2, ",--", label="input2")
plot(xorAO.T2, xorAO.CH2, ",-", label="XOR")

ax.set_yticklabels([])
ax.set_xticklabels([])
legend(loc=6)
grid()
savefig(dir + "grafici\\XORard.pdf")

figure(4)
fig,ax = subplots(1)

plot(AB.T1, AB.CH1, ",:", label="input1")
plot(AB.T2, AB.CH2, ",:", label="input2")
plot(xorAO.T2, xorAO.CH2, ",--", label="SUM")
plot(andAO.T2, andAO.CH2, ",-", label="CARRY")

ax.set_yticklabels([])
ax.set_xticklabels([])
legend(loc=6)

savefig(dir + "grafici\\ADDERard.pdf")

# show()
close('all')

###########################################################################

file = "astabileRes"
data = load_data(dir,file)
data_err = [mme(data[0], 'ohm'), mme(data[1], 'time', 'oscil')]
# data_err = [mme(data[0], 'ohm'), sqrt(((mme(data[1], 'time', 'oscil'))**2)+(3*data[1]/100)**2)]
tab=["$R_2$ [$\Omega$]","Period [s]"]

latex_table(dir, file, data, data_err, tab)

par, cov = fit_linear(data[0], data[1], dx=data_err[0], dy=data_err[1])
m, q = correlated_values(par, cov)

line = lambda x, m, q: m*x + q

x = linspace(min(data[0])*0.9, max(data[0])*1.05, 1000)
y = line(x, m.nominal_value, q.nominal_value)

chi2 = sum(((data[1] - line(data[0], m.nominal_value, q.nominal_value))/data_err[1])**2)
print(chi2)
print(cov[0,1]/(sqrt(cov[0,0]*cov[1,1])))
errorbar(data[0], data[1], xerr=data_err[0], yerr=data_err[1], fmt=',')
plot(x, y)

xlim(min(data[0])*0.88, max(data[0])*1.06)
ylim(min(data[1])*0.88, max(data[1])*1.06)
xlabel(tab[0], fontsize = 24)
ylabel(tab[1], fontsize = 24)
ticklabel_format(style='sci', axis='both', scilimits=(0,0))
grid()
savefig(dir + "./grafici/FITastabile.pdf")

########################################################################### parte astabile-stabile in serie...
#preambolo

C3 = ufloat(10.38e-9,mme(10.38e-9, "farad"))
R3 = ufloat(990, mme(990, "ohm"))

freq=1/(C3*R3)
print("frequanza di taglio=", freq)

#grafici...
O1 = OscilloscopeData(dir+"data\\4OutM.csv")
O2 = OscilloscopeData(dir+"data\\410030.csv")

figure(123)

plot(O1.T1, O1.CH1)
plot(O1.T2, O1.CH2)
grid()
figure(1234)

plot(O2.T1, O2.CH1)
plot(O2.T2, O2.CH2)
grid()

#analisi tempi...

myprint_dict={}

def printufloat(X):
    x=X.n
    y=X.s
    return str(x)+" \\pm "+str(y)

myprint_dict[uncertainties.ufloat]=printufloat
myprint_dict[uncertainties.core.AffineScalarFunc]=printufloat
myprint_dict[uncertainties.core.Variable]=printufloat

def printArray(x):
    s="array(["
    for i in x:
        s+=myprint(i)+","
    return s+"])"

myprint_dict[np.array]=printArray

def myprint(x):
    print(type(x), x, "<-----------")
    if type(x) in myprint_dict:
        print(type(x))
        return myprint_dict[type(x)](x)
    return str(x)

def my_latex_table(directory, file_, data, tab, verbose=True):
    """
        Parameters
        ----------

        Returns
        -------

    """
    with open(directory+"tabelle/tab_"+file_+".txt", "w") as text_file:
       # print(data)
        text_file.write("\\begin{tabular}{c")
        for z in range (1,len(tab)):
            text_file.write("|c")
        text_file.write("} \n")
        text_file.write("%s" % tab[0])
        for z in range (1,len(tab)):
            text_file.write(" & %s" % tab[z])
        text_file.write("\\\\\n\hline\n")
        for i in range (len(data[0])):
            text_file.write("%s" % myprint(data[0][i]))
            for j in range (1,len(tab)):
                print(i, j)
                print(data[j][i])
                text_file.write(" & %s" % myprint(data[j][i]))
            text_file.write("\\\\\n")
        text_file.write("\\end{tabular}")
        text_file.close()

file="bothRes.txt"
data=np.loadtxt(dir+"\\data\\bothRes.txt")
R1=data[:,0]
dR1=mme(R1, "ohm")
R2=data[:,1]
dR2=mme(R2, "ohm")
T1=data[:,2]*1e-6
dT1=np.ones(T1.shape)*1e-6
T2=data[:,3]*1e-6
dT2=np.ones(T2.shape)*1e-6
#approccio fit multipli...

from itertools import cycle
cycol = cycle('bgrcmk')

print("Fit per righe...")

figure(2)
# pylab.title("$R_1$ vs $T_{duty}$")
pylab.xlabel("$R_1$ [ohm]")
pylab.ylabel("$T_{duty}$ [sec]")
grid()

se={}
for i,j in zip(R2, dR2):
    if(not (i in se)):
        se[i]=j

results=[]
cond=[]
legs=[]
lines=[]

for i in se:
    mask=R2==i
    r1=R1[mask]
    dr1=dR1[mask]
    t1=T1[mask]
    dt1=dT1[mask]
    t2=T2[mask]
    dt2=dT2[mask]
    print(i)
    print(r1)
    par, cov = fit_linear(r1, t2, dx=dr1, dy=dt2)
    m, q = correlated_values(par, cov)
    chisq = sum((t2-line(r1, *par))**2/(par[0]**2*dr1**2+dt2**2))
    cond.append(ufloat(i, se[i]))
    results.append([par, cov, m, q, chisq])
    print("m={} , q={}".format(m, q))
    print("chiq={} su {} dof".format(chisq, len(r1)))
    domain=np.linspace(np.min(r1), np.max(r1))
    color=next(cycol)
    iline,=pylab.plot(domain, line(domain, *par), color=color, label="$R_2={}$".format(i))
    lines.append(iline)
    pylab.errorbar(r1, t2, dt2, dr1, ".", color=color)
    #pylab.plot(domain, line(domain, *par), color=color)
    legs.append("$R_2={}$".format(i))
    print("==============")

pylab.legend(legs, handles=lines)
pylab.savefig(dir + "./grafici/FitsRighe.pdf")

results=np.array(results)
ms=results[:,2]
qs=results[:,3]
chisqs=results[:,4]
nomi=["$R_2$", "$T/R$", "$T_0$", "$\\chi^2$"]
f=uncertainties.unumpy.nominal_values
ff=uncertainties.unumpy.std_devs
my_latex_table(dir, "fit per righe.txt", [cond, ms, qs, chisqs], nomi)

mnvs=uncertainties.unumpy.nominal_values(ms)
mstds=uncertainties.unumpy.std_devs(ms)
mfin, varmfin=fit_const_yerr(mnvs, mstds)

qnvs=uncertainties.unumpy.nominal_values(qs)
qstds=uncertainties.unumpy.std_devs(qs)
qfin, varqfin=fit_const_yerr(qnvs, qstds)

chifin1=sum((mfin-mnvs)**2/mstds**2)
chifin2=sum((qfin-qnvs)**2/qstds**2)

figure(3)
# pylab.title("$R_2$ vs m, $R_2$ vs q")
pylab.xlabel("$R_2$ [Ohm]")
pylab.subplot(311)
pylab.plot(list(se.keys()),(mfin-mnvs)/mstds, 'b.')
pylab.subplot(312)
pylab.plot(list(se.keys()), (qfin-qnvs)/qstds, 'b.')
pylab.savefig(dir + "./grafici/FitRighe.pdf",bbox_inches='tight')

mfin=uncertainties.ufloat(mfin, varmfin**0.5)
qfin=uncertainties.ufloat(qfin, varqfin**0.5)

print("------>>>>   m={} , q={}, chisq1={},  chisq2={}, dof={}".format(mfin, qfin, chifin1, chifin2, len(mnvs)))
print("c'è qualche problema...")

print("=======================")
print("=======================")

print("Fit per colonne...")

figure(4)
# pylab.title("$R_2$ vs $T_{duty}$")
pylab.xlabel("$R_2$ [ohm]")
pylab.ylabel("$Periodo$ [sec]")
grid()
results=[]
cond=[]
legs=[]
lines=[]

se={}
for i,j in zip(R1, dR1):
    if(not (i in se)):
        se[i]=j

for i in se:
    mask=R1==i
    r2=R2[mask]
    if(len(r2)>2):
        dr2=dR2[mask]
        t1=T1[mask]
        dt1=dT1[mask]
        t2=T2[mask]
        dt2=dT2[mask]
        print(i)
        print(r2)
        par, cov = fit_linear(r2, t1, dx=dr2, dy=dt1)
        m, q = correlated_values(par, cov)
        chisq = sum((t1-line(r2, *par))**2/(par[0]**2*dr2**2+dt1**2))
        cond.append(ufloat(i, se[i]))
        results.append([par, cov, m, q, chisq])
        print("m={} , q={}".format(m, q))
        print("chiq={} su {} dof".format(chisq, len(r2)))
        domain=np.linspace(np.min(r2), np.max(r2))
        color=next(cycol)
        iline,=pylab.plot(domain, line(domain, *par), label="$R_1={}$".format(i), color=color)
        lines.append(iline)
        pylab.errorbar(r2, t1, dt1, dr2, ".", color=color)
        legs.append("$R_1={}$".format(i))
        print("==============")

pylab.legend(legs, handles=lines)
pylab.savefig(dir + "./grafici/FitsColonne.pdf")

results=np.array(results)
ms=results[:,2]
qs=results[:,3]
chisqs=results[:,4]
nomi=["$R_1$", "$T/R$", "$T_0$", "$\\chi^2$"]
f=uncertainties.unumpy.nominal_values
ff=uncertainties.unumpy.std_devs
my_latex_table(dir, "fit per colonne.txt", [cond, ms, qs, chisqs], nomi)

mnvs=uncertainties.unumpy.nominal_values(ms)
mstds=uncertainties.unumpy.std_devs(ms)
mfin, varmfin=fit_const_yerr(mnvs, mstds)

qnvs=uncertainties.unumpy.nominal_values(qs)
qstds=uncertainties.unumpy.std_devs(qs)
qfin, varqfin=fit_const_yerr(qnvs, qstds)

chifin1=sum((mfin-mnvs)**2/mstds**2)
chifin2=sum((qfin-qnvs)**2/qstds**2)

figure(5)
# pylab.title("$R_1$ vs m, $R_1$ vs q")
pylab.xlabel("$R_1$ [Ohm]")
pylab.subplot(511)
pylab.plot(f(np.array(cond)),(mfin-mnvs)/mstds, 'b.')
pylab.subplot(512)
pylab.plot(f(np.array(cond)), (qfin-qnvs)/qstds, 'b.')
pylab.savefig(dir + "./grafici/FitColonne.pdf",bbox_inches='tight')

mfin=uncertainties.ufloat(mfin, varmfin**0.5)
qfin=uncertainties.ufloat(qfin, varqfin**0.5)

print("------>>>>   m={} , q={}, chisq1={},  chisq2={}, dof={}".format(mfin, qfin, chifin1, chifin2, len(mnvs)))
print("E' effettivamente lineare e non dipende da nulla...")

#last

R_2=ufloat(47.6+387, mme(47.6+387, "volt"))
R_1=ufloat(386, mme(386, "volt"))

print(R1, R2)

T__1=ufloat(100, 2.5)
T__2=ufloat(30, 2.5)
print(T__2/T__1)
#un approccio bidimensionale non ha troppo senso....


import sys
from collections import deque
n, m = map(int,sys.stdin.readline().split())
ground = [list(map(int,sys.stdin.readline().split())) for _ in range(n)]
dx = [0,1,0,-1]
dy = [1,0,-1,0]

def melt(visit):

  for i in range(n):
    for j in range(m):
      cnt = 0
      if ground[i][j] != 0:
        visit[i][j] = 1
        for k in range(4):
          cdx = i+dx[k]
          cdy = j+dy[k]
          if 0 <= cdx <n and 0<= cdy < m and ground[cdx][cdy] ==0 and visit[cdx][cdy] == 0:
            cnt+=1
        ground[i][j] = max(0,ground[i][j]-cnt)

def bfs(x,y):
  queue = deque([(x,y)])
  visit[x][y] = 0
  while queue:
    x,y = queue.popleft()
    for i in range(4):
      cdx = x + dx[i]
      cdy = y + dy[i]
      if 0 <=cdx < n and 0<=cdy <m and visit[cdx][cdy] == 1:
        visit[cdx][cdy] = 0
        queue.append((cdx,cdy))

year = 0
while True:
  count = 0
  visit = [[0]*m for _ in range(n)]
  melt(visit)
  year+=1

  for i in range(1,n-1):
    for j in range(1,m-1):
      if visit[i][j] == 1:
        count+=1
        bfs(i,j)

  if count == 0:
    print(0)
    break

  if count >= 2:
    print(year-1)
    break


from datetime import datetime
from django.http import JsonResponse
from rest_framework.parsers import JSONParser
from django.views.decorators.csrf import csrf_exempt
from rest_framework.decorators import api_view

from restapi.xmodels.index_models import Snippet
from restapi.serializers.index_serializer import SnippetSerializer

from drf_yasg.utils import swagger_auto_schema

@swagger_auto_schema(
    method='get',
    query_serializer=SnippetSerializer,
    responses={200: SnippetSerializer(many=True)},
    tags=['Users'],
)
@api_view(['GET', 'POST'])
@csrf_exempt
def index(request):
    snippets = Snippet(created=datetime.now(),
                       title="title", code="new code", linenos=4,
                       language="en-us", style=False)
    if request.method == 'GET':
        serializer = SnippetSerializer(snippets)
        response = JsonResponse(serializer.data, status=200)
        return response
    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            return JsonResponse(serializer.data, status=200)
        return JsonResponse(serializer.errors, status=400)


#
from typing import List
from tester import Tester

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        length = 1
        for num in nums:
            if num > nums[length - 1]:
                nums[length] = num
                length += 1
                continue

            start = 0
            end = length
            while start < end:
                m = (start + end) // 2
                if nums[m] < num:
                    start = m + 1
                else:
                    end = m

            nums[end] = num
        return length

t = Tester(Solution())

t.test(5, [10, 9, 2, 5, 3, 11, 12, 7, 10, 11])
t.test(4, [10, 9, 2, 5, 3, 7, 101, 18])
t.test(4, [0, 1, 0, 3, 2, 3])
t.test(1, [7, 7, 7, 7, 7, 7, 7])

t.report()


from django.shortcuts import render

from django.http import request
from django.http import response
from django.http import HttpResponse
# Create your views here.

from .forms import DaylightHours #NEW
# from .models import User  #NEW

from datetime import datetime
from math import sin , asin , cos , acos ,tan , atan , pi

def day_lenght(L:float ,fun_date=1 , fun_month=1 , fun_year=2023 ):
    """
    L:- lattitude in degrees

    """

    date1 = datetime(day=fun_date , month=fun_month,  year=fun_year)
    date2 = datetime(day= 1 ,month=1 ,year= fun_year)
    day_count = date1 - date2

    P = asin(0.39795*cos(0.2163108 + 2*atan(0.9671396*tan(0.00860*((day_count.days)-186)))))

    num = sin(0.8333*pi/180) + sin(L*pi/180)*sin(P)
    deno = cos(L*pi/180)*cos(P)

    D = 24 - (24/pi)*acos(num/deno)

    return D

def home_view(request):
    return HttpResponse('''This is Home Page .
                            Welcome !!
                            Hemlo World !!!
                        ''')

def home_view_temp(request):
    return render(request,"home.html")

def about_view_temp(request):
    return render(request ,"about.html")

def coffee_view_temp(request):
    return render(request , "coffee.html")

def p2_view_temp(request):
    return render(request , "p2.html")

def sr_view_temp(request):

    if request == "POST":
        fm = DaylightHours(request.POST)
        if fm.is_valid():
            dt = fm.data["date"]
            ltn = fm["latitude_south"]
            print(dt)
            print(ltn)
    else:
        fm = DaylightHours()

    return render(request , "srookie.html" , {'form':fm})

def se_view_temp(request):
    if request == "POST":
        fm = DaylightHours(request.POST)
        if fm.is_valid():
            dt = fm.data["date"]
            ltn = fm["latitude_south"]
            print(dt)
            print(ltn)
    else:
        fm = DaylightHours()

    return render(request , "senthu.html" , {'form':fm})

def result_view_temp(request):
    dt = int(request.POST["date"])
    mt = int(request.POST["month"])
    yt = int(request.POST["year"])
    lt  = float(request.POST["latitude"])

    context = {}
    context["date"] = (dt)
    context["month"] = mt
    context["year"] = yt
    context["latitude"] = float(lt)

    context["result"] = day_lenght(lt , dt ,mt , yt)

    print(context)

    # return HttpResponse(f"The date is {dt}")
    return render(request , "result.html" , context)

from datetime import datetime
from math import sin , asin , cos , acos ,tan , atan , pi

def day_lenght(L:float ,fun_date=1 , fun_month=1 , fun_year=2023 ):
    """
    L:- lattitude in degrees

    """

    date1 = datetime(day=fun_date , month=fun_month,  year=fun_year)
    date2 = datetime(day= 1 ,month=1 ,year= fun_year)
    day_count = date1 - date2

    P = asin(0.39795*cos(0.2163108 + 2*atan(0.9671396*tan(0.00860*((day_count.days)-186)))))

    num = sin(0.8333*pi/180) + sin(L*pi/180)*sin(P)
    deno = cos(L*pi/180)*cos(P)

    D = 24 - (24/pi)*acos(num/deno)

    return D


from flask import Flask,request,jsonify,make_response
from flask_restful import Resource,Api,reqparse
import mysql.connector

myDb = mysql.connector.connect(
    host = 'localhost',
    user = 'root',
    password = '',
    database = 'pegawai14'
)

myCursor = myDb.cursor()
app  = Flask(__name__)
api = Api(app)

class TodoList(Resource):
    def get(self):
        sql = '''
            SELECT *
            FROM bagian
        '''
        myCursor.execute(sql)
        hasilQuery = myCursor.fetchall()
        resp = jsonify(hasilQuery)
        return resp
    def post(self):
        _json = request.json
        _kode_Bag = _json['Kode_Bagi']
        _nama_Bagi = _json['Nama_Bagi']
        _gaji_Pokok = _json['Gaji_Pokok']
        sql = '''
            INSERT
            INTO bagian(Kode_Bagi,Nama_Bagi,Gaji_Pokok)
            VALUES(%s,%s,%s)
        '''
        inputParameter = (_kode_Bag,_nama_Bagi,_gaji_Pokok,)
        myCursor.execute(sql,inputParameter)
        myDb.commit()
        respone = jsonify('Employee added successfully!')
        respone.status_code = 200
        return respone
    def delete(self):
        _json = request.json
        _kode_Bag = _json['Kode_Bagi']
        sql = '''
            DELETE FROM bagian
            WHERE Kode_Bagi = %s
        '''
        inputParameter = (_kode_Bag,)
        myCursor.execute(sql,inputParameter)
        myDb.commit()
        respone = jsonify('Employee added successfully!')
        respone.status_code = 200
        return respone
class Todo(Resource):
    def get(self,inputId):
        sql = '''
                   SELECT *
                   FROM bagian
                   WHERE Kode_Bagi = %s
               '''
        inputParameter = (inputId,)
        myCursor.execute(sql,inputParameter)
        hasilQuery = myCursor.fetchall()
        resp = jsonify(hasilQuery)
        return resp
    def delete(self,inputId):
        sql = '''
            DELETE FROM bagian
            WHERE Kode_Bagi = %s
        '''
        inputParameter = (inputId,)
        myCursor.execute(sql,inputParameter)
        myDb.commit()
        return '', 204
api.add_resource(TodoList,'/bagian')
api.add_resource(Todo,'/bagian/<inputId>')

if __name__ == '__main__':
    app.run(debug=True)


import matplotlib.pyplot as plt

def plot_component(results, results_train, component, ts_data, ts_idx, cs):
    for j, r in enumerate(results):
        df = r["predictions"][ts_idx]
        plt.plot(ts_data[ts_idx]["future"]["t"], df[component], color=cs[j])
        if results_train is not None:
            df_train = results_train[j]["predictions"][ts_idx]
            plt.plot(ts_data[ts_idx]["history"]["t"],
                     df_train[component].as_matrix(), color=cs[j])
    plt.legend(loc=2, prop={'size': 24})
    plt.show()

def visualize_results(results, results_train, ts_data, ts_idx=0):
    ts = ts_data[ts_idx]
    cs = ['C1', 'C3', 'C5']

    # train and test split
    plt.axvline(x=1., color='k', linestyle='--')

    # True data
    plt.plot(ts["history"]["t"], ts["history"]["y_scaled"].as_matrix(),
             lw=1, color='C2', label='y_true')
    plt.plot(ts["future"]["t"], ts["future"]["y_scaled"].as_matrix(),
             lw=1, color='C2', label='_nolegend_')

    for j, r in enumerate(results):
        df = r["predictions"][ts_idx]
        plt.plot(ts["future"]["t"], df["y"].as_matrix(),
                 label="Model %d" % j, color=cs[j])
        if results_train is not None:
            df_train = results_train[j]["predictions"][ts_idx]
            plt.plot(ts["history"]["t"], df_train["y"],
                     color=cs[j], label='_nolegend_')
    plt.legend(loc=2, prop={'size': 24})
    # plt.legend(handles=[y_true_train,y_true_test])
    plt.show()

    plt.subplot(411)
    plot_component(results, results_train, "trend", ts_data, ts_idx, cs)
    plt.subplot(412)
    plot_component(results, results_train, "yearly", ts_data, ts_idx, cs)
    plt.subplot(413)
    plot_component(results, results_train, "weekly", ts_data, ts_idx, cs)
    plt.subplot(414)

    # Residual
    for j, r in enumerate(results):
        df = r["predictions"][ts_idx]
        plt.plot(ts["future"]["t"],
                 ts["future"]["y_scaled"].as_matrix() - df["y"].as_matrix(),
                 color=cs[j], label='residual')
        if results_train is not None:
            df_train = results_train[j]["predictions"][ts_idx]
            plt.plot(ts["history"]["t"],
                     ts["history"]["y_scaled"].as_matrix() -
                     df_train["y"].as_matrix(), color=cs[j])
    plt.legend(loc=2, prop={'size': 24})
    plt.show()


from collections import defaultdict

class Rule():
    def __init__(self, raw: str):
        # departure location: 50-692 or 705-969
        self.field = raw.split(":")[0]
        self.ranges = [list(map(int, x.split("-"))) for x in raw.split(": ")[1].split(" or ")]

class Ticket():
    def __init__(self, raw: str):
        # 460,805,566,621,266,233,771,202,368,751,150,558,184,248,457,424,351,184,821,623
        self.values = list(map(int, raw.split(",")))

    def validate(self, rules: [Rule]) -> int:
        validations = defaultdict(list)
        for rule in rules:
            for _range in rule.ranges:
                for value in self.values:
                    if not _range[0] <= value <= _range[1]:
                        validations[value].append(False)
                    else:
                        validations[value].append(True)

        errorRate = 0
        for validation in validations.items():
            if True not in validation[1]:
                errorRate += validation[0]

        return errorRate

    def findValidFields(self, rules: [Rule]) -> dict:
        validFields = defaultdict(list)
        for rule in rules:
            for _range in rule.ranges:
                for value in enumerate(self.values):
                    if _range[0] <= value[1] <= _range[1]:
                        validFields[rule.field].append(value[0])

        return validFields


import pika

def publish_with_delay(message, delay_ms, expiration_ms, delayed_exchange, delayed_queue):
    connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
    channel = connection.channel()

    channel.exchange_declare(exchange=delayed_exchange, exchange_type='x-delayed-message', arguments={
        'x-delayed-type': 'direct'
    })

    channel.queue_declare(queue=delayed_queue, arguments={
        'x-delayed-type': 'direct',
        'x-delayed-message-type': 'direct'
    })

    channel.queue_bind(queue=delayed_queue, exchange=delayed_exchange, routing_key='delayed_queue')

    properties = pika.BasicProperties(
        delivery_mode = 2,  # Make messages persistent
        expiration = str(expiration_ms)  # Set the expiration property to the duration in milliseconds
    )

    channel.basic_publish(
        exchange = delayed_exchange,
        routing_key='delayed_queue',
        body=message,
        properties=properties
    )

    print("Message '{}' published with a delay of {} ms and expiration of {} ms".format(message, delay_ms, expiration_ms))

    connection.close()


# 保存模型/加载模型
from keras.models import load_model
import matplotlib.pyplot as plt

# model.save('model_p01')  # creates a HDF5 file 'dl_T.h5'
# del model  # 删除现有模型
# # 加载已保存模型
model = load_model('model_p01')
acc = h.history['accuracy']
val_acc = h.history['val_accuracy']
loss = h.history['loss']
val_loss = h.history['val_loss']
epochs = range(len(acc))

plt.plot(epochs,acc, 'b', label='Training accuracy')
plt.plot(epochs, val_acc, 'r', label='validation accuracy')
plt.title('Training and validation accuracy')
plt.legend(loc='lower right')
plt.figure()

plt.plot(epochs, loss, 'r', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()


from os import path
from pandas import DataFrame, read_csv
from io import StringIO
import json

from pytimeparse import parse as pt_parse

from contentai_metadata_flatten.parsers import Flatten

class Parser(Flatten):
    def __init__(self, path_content, logger=None):
        super().__init__(path_content, logger=logger)
        self.EXTRACTOR = "dsai_activity_classifier"
        self.TAG_TYPE = "tag"

    @staticmethod
    def known_types():
        """Return the output types for this generator
        :return: list.  List of output types (file types) for this generator
        """
        return ['tag']

    def parse(self, run_options):
        """Flatten activity classifier score results

        :param: run_options (dict): specific runtime information
        :returns: (DataFrame): DataFrame on successful decoding and export, None (or exception) otherwise
        """
        dict_data = self.get_extractor_results(self.EXTRACTOR, "data.json", is_json=True)
        if not dict_data:
            if run_options["verbose"]:
                self.logger.critical(f"Empty result string for extractor '{self.EXTRACTOR}', aborting")
            return None

        # "results": [
        #     {
        #         "time_begin": 0,
        #         "time_end": 1.5,
        #         "type_audio": "",
        #         "type_video": "dsai_videocnn",
        #         "score": 0.28173,
        #         "class": "BuildingExplode"
        #     },

        list_items = []

        if dict_data is None or 'results' not in dict_data or 'config' not in dict_data:
            self.logger.critical(f"Missing nested 'results' from source '{self.EXTRACTOR}'")
            return None
        for local_obj in dict_data["results"]:
            if "class" in local_obj and "score" in local_obj:  # validate object
                time_begin = float(local_obj['time_begin'])
                time_end = float(local_obj['time_end'])
                details_obj = {}
                source_type = 'video'
                if 'type_video' in local_obj and len(local_obj['type_video']) > 0:
                    details_obj['video'] = local_obj['type_video']
                if 'type_audio' in local_obj and len(local_obj['type_audio']) > 0:
                    details_obj['audio'] = local_obj['type_audio']
                    if "video" not in details_obj:
                        source_type = 'audio'
                list_items.append({"time_begin": time_begin, "source_event": source_type, "tag_type": self.TAG_TYPE,
                    "time_end": time_end, "time_event": time_begin, "tag": local_obj["class"],
                    "score":  local_obj['score'], "details": json.dumps(details_obj),
                    "extractor": self.EXTRACTOR})

        if len(list_items) > 0:   # return the whole thing as dataframe
            return DataFrame(list_items)

        if run_options["verbose"]:
            self.logger.critical(f"No valid events detected for '{self.EXTRACTOR}'")
        return None


'''
Created on Jun 2, 2010

@author: jnaous
'''
from django.utils.importlib import import_module
from django.db.models import signals
from django.conf import settings
import logging

logger = logging.getLogger("permissions.management")

# XXX: Hack!
def post_syncdb_run(sender, **kwargs):
    """Look for permissions.run in each app and call it.

    This function will wait till the last installed app, and then
    search for all permissions modules, and try to call a C{run} function
    in them if found.
    """
    post_syncdb_run.queued = getattr(post_syncdb_run, "processed", [])
    post_syncdb_run.perms_installed = getattr(
        post_syncdb_run, "perms_installed", False)

    app = sender.__name__.split(".models")[0]

    # installed!
    if app == "expedient.common.permissions":
        post_syncdb_run.perms_installed = True

    post_syncdb_run.queued.append(app)

    # need to wait until we syncdb the permissions app
    if post_syncdb_run.perms_installed:
        for app in post_syncdb_run.queued:
            logger.debug("Checking app %s for permissions" % app)
            try:
                mod = import_module("%s.permissions" % app)
            except ImportError as exc:
                msg = exc.args[0]
                if not msg.startswith('No module named') or\
                "permissions" not in msg:
                    raise
                else:
                    continue
            try:
                mod.run()
                logger.debug("Ran permissions.run() for %s" % app)
            except AttributeError as exc:
                msg = "%s" % exc
                if msg == "'module' object has no attribute 'run'":
                    logger.warn(
                        "Function called 'run' missing from %s.permissions."\
                        " Skipping." % app
                    )
                else:
                    raise
        post_syncdb_run.queued = []

signals.post_syncdb.connect(post_syncdb_run)


# autobyteus/source_code_tree/file_explorer/traversal_ignore_strategy/git_ignore_strategy.py

import os
import fnmatch
import pathlib

from autobyteus.source_code_tree.file_explorer.traversal_ignore_strategy.traversal_ignore_strategy import TraversalIgnoreStrategy

class GitIgnoreStrategy(TraversalIgnoreStrategy):
    """
    A strategy to ignore files and folders specified in a .gitignore file.
    """

    def __init__(self, root_path: str):
        """
        Initialize GitIgnoreStrategy.

        Args:
            root_path (str): The root path of the workspace.
        """
        self.root_path = pathlib.Path(root_path)  # Store the root path as a Path object
        self.ignore_patterns = []
        gitignore_path = os.path.join(root_path, '.gitignore')
        if os.path.exists(gitignore_path):
            with open(gitignore_path, 'r') as gitignore_file:
                self.ignore_patterns = gitignore_file.read().splitlines()

    def should_ignore(self, path: str) -> bool:
        """
        Determines if a file or folder should be ignored based on patterns specified in a .gitignore file.

        The method iterates over all the ignore patterns. For each pattern, if it's a directory pattern
        (ending with '/'), it removes the trailing slash and appends one to the path (if it's not already there)
        before performing the match. For file patterns, it performs the match directly.

        Args:
            path (str): The path of the file or folder.

        Returns:
            bool: True if the file or folder matches a pattern in the .gitignore file and should be ignored,
                  False otherwise.
        """
        relative_path = pathlib.Path(path).relative_to(self.root_path)  # Compute the relative path
        for pattern in self.ignore_patterns:
            if pattern.endswith('/'):  # The pattern is for a directory
                if fnmatch.fnmatch(os.path.join(relative_path, ''), pattern):
                    return True
            else:  # The pattern is for a file
                if fnmatch.fnmatch(relative_path, pattern):
                    return True
        return False


import logging
import os
import time
import datetime

import boto3
import pystache
import yaml
import simplejson
from botocore.exceptions import NoCredentialsError, ClientError

from cfnstack.CFNStack import CFNStack

"""
StackGlue glues cloudformation stacks together and provides ability to create/destroy stacks based on dependency defined in YAML file
StackGlue class has methods to read YAML file where cloudformation stacks are listed with dependencies
and sort the cloudformation stacks based on dependencies and action methods to create, update and delete
cloudformation stacks listed in YAML file
"""

class StackGlue(object):
    def __init__(self, yamlfile, profile):
        self.logger = logging.getLogger(__name__)

        if profile and not profile.isspace():
            config_profile = profile
        else:
            config_profile = 'default'

        yamlconfig = open(yamlfile, 'r')
        render_yaml = pystache.render(yamlconfig.read(), dict(os.environ))
        self.stackDict = yaml.safe_load(render_yaml)

        # There will be only one global stack name
        toplevel_stack_count = len(self.stackDict.keys())
        if toplevel_stack_count != 1:
            error_message = ("Need one and only global stack name at the top level, found %s")
            self.logger.critical(error_message % toplevel_stack_count)
            exit(1)

        self.name = list(self.stackDict.keys())[0]

        if 'region' in self.stackDict[self.name]:
            self.region = self.stackDict[self.name]['region']
        else:
            self.logger.critical("No region mentioned in the stack. Please specify region")
            exit(1)

        # Verifying account id of key and stack account id

        # if 'account_id' in self.stackDict[self.name]:
        #     iam = boto3.resource('iam')
        #     current_user_arn = iam.CurrentUser().arn
        #     account_id = current_user_arn.split(':')[4]
        #
        #     if account_id != str(self.stackDict[self.name]['account_id']):
        #         self.logger.critical(
        #             'Account ID of the stack does not match the account ID of the aws credentials used.')
        #         exit(1)

        # Get the environment
        if 'environment' in self.stackDict[self.name]:
            self.environment = self.stackDict[self.name]['environment'].lower()
        else:
            # Assign default as DEV
            self.environment = 'dev'

        # Get and verify SNS topic
        self.sns_topic_arn = self.stackDict[self.name].get('sns-topic-arn', [])
        if isinstance(self.sns_topic_arn, str):
            self.sns_topic_arn = [self.sns_topic_arn]
        for topic in self.sns_topic_arn:
            if topic.split(':')[3] != self.region:
                self.logger.critical('SNS Topic %s is not in the %s region' % (topic, self.region))
                exit(1)

        self.global_tags = self.stackDict[self.name].get('tags', {})
        # print(self.global_tags['project'])

        # Array for holding CFNStack objects
        self.stack_objs = []

        self.cf_stacks = list(self.stackDict[self.name]['stacks'].keys())

        # Get all existing cloudformation stack details
        try:
            self.aws_session = boto3.Session(profile_name=config_profile)
            self.cfn_conn = self.aws_session.resource("cloudformation")
            self.cfn_all_stacks = self.cfn_conn.stacks.all()
        except NoCredentialsError as exception:
            self.logger.critical("No Credentials found for connecting to cloudformation: %s" % exception)
            exit(1)

        for stack_name in self.cf_stacks:
            one_stack = self.stackDict[self.name]['stacks'][stack_name]
            if type(one_stack) is dict:
                if one_stack.get('disable', False):
                    self.logger.warning("Stack %s is disabled by configuration, skipping..." % stack_name)
                    continue

            local_sns_arn = one_stack.get('sns-topic-arn', self.sns_topic_arn)
            if isinstance(local_sns_arn, str):
                local_sns_arn = [local_sns_arn]

            for topic in local_sns_arn:
                if topic.split(':')[3] != self.region:
                    self.logger.critical(
                        "SNS topic '%s' for Stack '%s' is not in the '%s' region " % (topic, stack_name, self.region))
                    exit(1)

            local_tags = one_stack.get('tags', {})

            self.merged_tags = self.global_tags.copy()
            self.merged_tags.update(local_tags.items())

            # Add static application tag
            self.merged_tags['Environment'] = self.environment.upper()

            if 'cf_template' in one_stack:
                self.stack_objs.append(
                    CFNStack(
                        stack_glue_name=self.name,
                        aws_session = self.aws_session,
                        name=stack_name,
                        environment=self.environment,
                        params=one_stack.get('params'),
                        template_name=one_stack['cf_template'],
                        region=self.region,
                        sns_topic_arn=local_sns_arn,
                        depends_on=one_stack.get('depends'),
                        tags=self.merged_tags
                    )
                )

    # Sort Cloudformation stacks by dependencies listed in YAML file
    def sort_cf_stacks_by_deps(self):
        """
        Sort the array of stack_objs so they are in dependency order
        """
        sorted_stacks = []
        dep_graph = {}
        no_deps = []

        # Add all stacks without dependencies in no_deps
        for stack in self.stack_objs:
            if stack.depends_on is None:
                no_deps.append(stack)
            else:
                dep_graph[stack.name] = stack.depends_on[:]

        while len(no_deps) > 0:
            stack = no_deps.pop()
            sorted_stacks.append(stack)
            for node in list(dep_graph):
                for deps in dep_graph[node]:
                    if stack.cfn_stack_name == deps:
                        dep_graph[node].remove(stack.cfn_stack_name)
                        if len(dep_graph[node]) < 1:
                            for stack_obj in self.stack_objs:
                                if stack_obj.name == node:
                                    no_deps.append(stack_obj)
                            del dep_graph[node]
        if len(dep_graph) > 0:
            self.logger.critical(
                "could not resolve dependency order. Either circular dependency or dependency on stack not in yaml file")
            exit(1)
        else:
            self.stack_objs = sorted_stacks
            return True

    # Apply - Create stacks if does not exists in AWS cloudformation and update the stack with updated template if stack already exists in cloudformation
    def apply(self, stack_name=None):
        for stack in self.stack_objs:
            if stack_name and stack.name != stack_name:
                continue
            self.logger.info("Determining whether stack needs to be created or updated")

            if not stack.exists_in_cfn(self.cfn_all_stacks):
                self.logger.info("Stack %s does not exists in CloudFormation. Stack %s is going to be created" % (
                stack.name, stack.name))
                self.create(stack.name)
            else:
                self.logger.info(
                    "Stack %s exists in CloudFormation. Checking wheter there is any change in the cloudformation template or parameters" % stack.name)
                self.update(stack.name)

    # Create cloudformation stack and this function is called from apply.
    def create(self, stack_name=None):
        """
        Create all stacks in the yaml file based on dependency order.
        Any stack already exists skip the stack creation
        """

        for stack in self.stack_objs:
            if stack_name and stack.name != stack_name:
                continue
            self.logger.info("Starting checks for creation of stack %s" % stack.name)

            if stack.exists_in_cfn(self.cfn_all_stacks):
                self.logger.critical("Stack %s already exists in cloudformation, skipping..." % stack.name)
            else:
                if stack.dependencies_met(self.cfn_all_stacks) is False:
                    self.logger.critical("Dependencies for stack %s is not met and exiting..." % stack.name)
                    exit(1)
                if not stack.populate_params(self.cfn_all_stacks):
                    self.logger.critical("Could not determine correct parameters for stack %s" % stack.name)
                    exit(1)

                stack.read_template()
                self.logger.info("Creating: %s, and its parameters : %s" % (stack.cfn_stack_name, stack.params))
                try:
                    self.cfn_conn.create_stack(
                        StackName=stack.cfn_stack_name,
                        TemplateBody=stack.template_body,
                        Parameters=stack.params,
                        Capabilities=['CAPABILITY_IAM'],
                        NotificationARNs=stack.sns_topic_arn,
                        OnFailure='DELETE',
                        Tags=stack.tags
                    )
                except Exception as exception:
                    self.logger.critical("Creating stack %s failed. Error: %s" % (stack.cfn_stack_name, exception))
                    exit(1)

                create_result = self.watch_events(stack.cfn_stack_name, "CREATE_IN_PROGRESS")
                if create_result != "CREATE_COMPLETE":
                    self.logger.critical("Stack did not create correctly, status is now %s" % create_result)
                    exit(1)

                self.logger.info("Finished creating stack: %s" % stack.cfn_stack_name)
                self.cfn_all_stacks = self.cfn_conn.stacks.all()

    # Update cloudfromation stack if already exists in AWS cloudformation
    def update(self, stack_name=None):
        for stack in self.stack_objs:
            if stack_name and stack.name != stack_name:
                continue
            self.logger.info("Starting checks for update of stack %s" % stack.name)

            if not stack.exists_in_cfn(self.cfn_all_stacks):
                self.logger.critical(
                    "Stack %s does not exists in cloudformation, can't update non-existing stack, skipping..." % stack.name)
            else:
                if stack.dependencies_met(self.cfn_all_stacks) is False:
                    self.logger.critical("Dependencies for stack %s is not met and exiting..." % stack.name)
                    exit(1)
                if not stack.populate_params(self.cfn_all_stacks):
                    self.logger.critical("Could not determine correct parameters for stack %s" % stack.name)
                    exit(1)

                stack.read_template()

                template_up_to_date = stack.template_uptodate(self.cfn_all_stacks)
                params_up_to_date = stack.params_uptodate(self.cfn_all_stacks)

                self.logger.info("Stack is up to date: %s" % (template_up_to_date and params_up_to_date))

                if template_up_to_date and params_up_to_date:
                    self.logger.info("Stack '%s' is already up to date with cloudformation. Skipping..." % stack.name)
                else:
                    self.logger.info("Template or parameter for stack %s has changed." % stack.name)
                    self.logger.info("Starting update of stack %s with parameters: %s" % (stack.name, stack.params))

                    # Validate template step can be added here

                    try:
                        self.cfn_conn.Stack(stack.cfn_stack_name).update(
                            TemplateBody=stack.template_body,
                            Parameters=stack.params,
                            Capabilities=['CAPABILITY_IAM'],
                            NotificationARNs=stack.sns_topic_arn
                        )
                    except ClientError as exception:
                        if (str(exception.response['Error']['Message']) == "No updates are to be performed."):
                            self.logger.error(
                                "CloudFormation has no updates to perform on resources of stack %s. Continue with next stack if exists..." % stack.name)
                            continue
                        else:
                            self.logger.critical(
                                "Updating stack %s failed. Error: %s" % (stack.cfn_stack_name, exception))
                            exit(1)

                    update_result = self.watch_events(
                        stack.cfn_stack_name, [
                            "UPDATE_IN_PROGRESS",
                            "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS"])
                    if update_result != "UPDATE_COMPLETE":
                        self.logger.critical(
                            "Stack didn't update correctly, status is now %s"
                            % update_result)
                        exit(1)

                    self.logger.info(
                        "Finished updating stack: %s" % stack.cfn_stack_name)

            # avoid getting rate limited
            time.sleep(2)

    #List CF change sets created in a stack
    def listcs(self,stack_name=None):
        for stack in self.stack_objs:
            self.logger.info(stack)
            if stack_name and stack.name != stack_name:
                continue
            self.logger.info("Starting to retrieve changesets for %s" % stack.name)

            if not stack.exists_in_cfn(self.cfn_all_stacks):
                self.logger.critical(
                    "Stack %s does not exists in cloudformation, can't list change sets from non-existing stack, skipping..." % stack.name)
            else:
                if stack.dependencies_met(self.cfn_all_stacks) is False:
                    self.logger.critical("Dependencies for stack %s is not met and exiting..." % stack.name)
                    exit(1)
                if not stack.populate_params(self.cfn_all_stacks):
                    self.logger.critical("Could not determine correct parameters for stack %s" % stack.name)
                    exit(1)

            cf_client = self.cfn_conn.meta.client;

            try:
                stackcs = cf_client.list_change_sets(StackName=stack.cfn_stack_name)
            except  Exception as exception:
                self.logger.critical("Can't list change sets for stack %s. Error: %s" % (stack.cfn_stack_name, exception))
                exit(1)

            enco = lambda obj: (
                obj.isoformat()
                if isinstance(obj, datetime.datetime)
                    or isinstance(obj, datetime.date)
                else None
            )

            for csval in stackcs['Summaries']:
                self.logger.info("\nHere is the description of change set \"%s\" for stack \"%s\", created on %s"
                                     % (csval['ChangeSetName'],csval['StackName'],csval['CreationTime']))
                self.logger.info("\n"+simplejson.dumps(csval,sort_keys=False,indent=4,default=enco))
                self.logger.info("\n-----------------------------------------------------------------------------\n")
                try:
                    stackcs = cf_client.describe_change_set(ChangeSetName=csval['ChangeSetName'],StackName=csval['StackName'])
                except  Exception as exception:
                    self.logger.critical(
                            "Can't describe change sets for stack %s. Error: %s" % (stack.cfn_stack_name, exception))
                    exit(1)
                self.logger.info("\nDetails of changes in the change set \"%s\" for stack \"%s\"" % (csval['ChangeSetName'],csval['StackName']) )
                self.logger.info("\n"+simplejson.dumps(stackcs, sort_keys=False, indent=4, default=enco))
                self.logger.info("\n********************************************************************\n")

    #Apply or execute changeset to a stack
    def applycs(self, stack_name=None, changesetname=None):
        for stack in self.stack_objs:
            if stack_name and stack.name != stack_name:
                continue
            self.logger.info("Starting to retrieve changesets for %s" % stack.name)

        if not stack.exists_in_cfn(self.cfn_all_stacks):
            self.logger.critical(
                "Stack %s does not exists in cloudformation, can't apply change sets on non-existing stack, skipping..." % stack.name)
        else:
            if stack.dependencies_met(self.cfn_all_stacks) is False:
                self.logger.critical("Dependencies for stack %s is not met and exiting..." % stack.name)
                exit(1)
            if not stack.populate_params(self.cfn_all_stacks):
                self.logger.critical("Could not determine correct parameters for stack %s" % stack.name)
                exit(1)

        cf_client = self.cfn_conn.meta.client;

        try:
            stackcs = cf_client.list_change_sets(StackName=stack.cfn_stack_name)
        except  Exception as exception:
            self.logger.critical("Can't list change sets for stack %s. Error: %s" % (stack.cfn_stack_name, exception))
            exit(1)

        if len(stackcs['Summaries'])==0:
            self.logger.info("No changesets are available to apply. Exiting with error")
            exit(1)

        cs_exists = 0
        for csval in stackcs['Summaries']:
            if stack.name == stack.name:
                if csval['ChangeSetName'] == changesetname:
                    self.logger.critical("Change set \"%s\" is available in stack \"%s\"" % (csval['ChangeSetName'],csval['StackName']))
                    cs_exists = 1

        if cs_exists == 0:
            self.logger.critical("Can't find change set called \"%s\"" % changesetname)
            exit(1)
        else:
            try:
                cf_client.execute_change_set(ChangeSetName=changesetname, StackName=stack.cfn_stack_name)
            except  Exception as exception:
                self.logger.critical(
                        "Can't execute change sets for stack %s. Error: %s" % (stack.cfn_stack_name, exception))
                exit(1)

        update_result = self.watch_events(
                stack.cfn_stack_name, [
                        "UPDATE_IN_PROGRESS",
                        "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS"])
        if update_result != "UPDATE_COMPLETE":
                self.logger.critical(
                        "Stack didn't update correctly, status is now %s"
                        % update_result)
                exit(1)

        self.logger.info(
                    "Finished updating stack %s using change set: %s" % (stack.cfn_stack_name,changesetname))

    #Create change set for a CF stack
    def createcs(self, stack_name=None, changesetname=None):
        for stack in self.stack_objs:
            if stack_name and stack.name != stack_name:
                continue

            if not stack.exists_in_cfn(self.cfn_all_stacks):
                self.logger.critical(
                    "Stack %s does not exists in cloudformation, can't create change sets on non-existing stack, skipping..." % stack.name)
            else:
                if stack.dependencies_met(self.cfn_all_stacks) is False:
                    self.logger.critical("Dependencies for stack %s is not met and exiting..." % stack.name)
                    exit(1)
                if not stack.populate_params(self.cfn_all_stacks):
                    self.logger.critical("Could not determine correct parameters for stack %s" % stack.name)
                    exit(1)

            cf_client = self.cfn_conn.meta.client;

            current_time = datetime.datetime.now().isoformat()
            description_txt = "Change Set "+changesetname+" is created for "+stack.cfn_stack_name+" at "+current_time

            stack.read_template()
            self.logger.info("Creating Changeset: %s for stack: %s, and its parameters : %s" % (changesetname,stack.cfn_stack_name, stack.params))

            try:
                cf_client.create_change_set(
                    StackName=stack.cfn_stack_name,
                    TemplateBody=stack.template_body,
                    Parameters=stack.params,
                    Capabilities=['CAPABILITY_IAM'],
                    NotificationARNs=stack.sns_topic_arn,
                    Tags=stack.tags,
                    ChangeSetName = changesetname,
                    Description = description_txt
                )
            except Exception as exception:
                self.logger.critical(
                    "Can't create change sets for stack %s. Error: %s" % (stack.cfn_stack_name, exception))
                exit(1)

            self.logger.info("Changeset %s for stack %s is successfully created" % (changesetname, stack.cfn_stack_name))

    #Delete change set from a CF stack
    def deletecs(self, stack_name=None, changesetname=None):
        for stack in self.stack_objs:
            if stack_name and stack.name != stack_name:
                continue

            if not stack.exists_in_cfn(self.cfn_all_stacks):
                self.logger.critical(
                    "Stack %s does not exists in cloudformation, can't delete change sets on non-existing stack, skipping..." % stack.name)
            else:
                if stack.dependencies_met(self.cfn_all_stacks) is False:
                    self.logger.critical("Dependencies for stack %s is not met and exiting..." % stack.name)
                    exit(1)
                if not stack.populate_params(self.cfn_all_stacks):
                    self.logger.critical("Could not determine correct parameters for stack %s" % stack.name)
                    exit(1)

            cf_client = self.cfn_conn.meta.client;

            self.logger.info("Delete Changeset: %s for stack: %s, and its parameters : %s" % (
            changesetname, stack.cfn_stack_name, stack.params))

            try:
                cf_client.delete_change_set(
                    StackName=stack.cfn_stack_name,
                    ChangeSetName=changesetname
                )
            except Exception as exception:
                self.logger.critical(
                    "Can't delete change sets for stack %s. Error: %s" % (stack.cfn_stack_name, exception))
                exit(1)

            self.logger.info(
                "Changeset %s for stack %s is successfully deleted" % (changesetname, stack.cfn_stack_name))

    #Delete cloudformation stack
    def delete(self, stack_name=None):
        """
        Delete all the stacks from cloudformation.
        Delete the stack in reverse dependency order
        """
        for stack in reversed(self.stack_objs):
            if stack_name and stack.name != stack_name:
                continue
            self.logger.info("Starting checks for creation of stack %s" % stack.name)

            if not stack.exists_in_cfn(self.cfn_all_stacks):
                self.logger.critical("Stack %s does not exist in cloudformation, skipping..." % stack.name)
            else:
                self.logger.info("Starting to delete stacks %s" % stack.name)
                try:
                    self.cfn_conn.Stack(stack.cfn_stack_name).delete()
                except  Exception as exception:
                    self.logger.critical("Deleting stack %s failed. Error: %s" % (stack.cfn_stack_name, exception))
                    exit(1)

                delete_result = self.watch_events(stack.cfn_stack_name, "DELETE_IN_PROGRESS")

                if (delete_result != "DELETE_COMPLETE" and delete_result != "STACK_GONE"):
                    self.logger.critical("Stack didn't get deleted correctly, Status is now %s", delete_result)
                    exit(1)

                self.logger.info("Finished deleting Stack: %s", stack.cfn_stack_name)
                self.cfn_all_stacks = self.cfn_conn.stacks.all()

    # Watch cloudformation events for all action
    def watch_events(self, stack_name, while_status):
        """
        Stay and watch cloudformation events till 'while_status'
        """
        cfstack_obj = self.cfn_conn.Stack(stack_name)
        first_events = []

        try:
            cfstack_obj.reload()
            events = self.cfn_conn.Stack(stack_name).events.all()
        except ClientError as exception:
            if (str(exception.response['Error']['Message']) == "Stack with id %s does not exist" % (stack_name)):
                return "STACK_GONE"

        for evt in first_events:
            first_events.append(evt)

        self.logger.info("All events for the stack - %s :", self.name)

        try:
            for evt in reversed(first_events):
                self.logger.info("%s %s %s %s %s %s" % (
                    evt.timestamp.isoformat(),
                    evt.resource_status,
                    evt.resource_type,
                    evt.logical_resource_id,
                    evt.physical_resource_id,
                    evt.resource_status_reason,
                ))
        except ClientError as exception:
            self.logger.critical("Error reading events list : " + str(exception))

        status = str(cfstack_obj.stack_status)

        while status in while_status:
            self.logger.info("Fetching new events for the stack - %s :", self.name)
            new_events = []
            event_to_log = []
            try:
                cfstack_obj.reload()
                events = self.cfn_conn.Stack(stack_name).events.all()
            except ClientError as exception:
                if (str(exception.response['Error']['Message']) == "Stack with id %s does not exist" % (stack_name)):
                    return "STACK_GONE"

            for evt in events:
                new_events.append(evt)

            event_to_log = [evt for evt in new_events if evt not in first_events]

            try:
                for evt in reversed(event_to_log):
                    self.logger.info("%s %s %s %s %s %s" % (
                        evt.timestamp.isoformat(),
                        evt.resource_status,
                        evt.resource_type,
                        evt.logical_resource_id,
                        evt.physical_resource_id,
                        evt.resource_status_reason,
                    ))
            except ClientError as exception:
                self.logger.critical("Error reading events list : " + str(exception))

            try:
                status = str(cfstack_obj.stack_status)
            except ClientError as exception:
                if (str(exception.response['Error']['Message']) == "Stack with id %s does not exist" % (stack_name)):
                    return "STACK_GONE"

            first_events = new_events

            self.logger.info("Waiting 5 Sec to fetch log for the stack - %s :", self.name)

            time.sleep(5)

        return status