analytics.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. import json
  6. from studio import checklist
  7. from studio import story
  8. from studio import schedule
  9. from settings import settings
  10. from settings import talk
  11. def iftime(string):
  12. if len(string) != len("00:00:00"):
  13. return False
  14. if len(string.split(":")) != 3:
  15. return False
  16. try:
  17. for n, i in enumerate(string.split(":")):
  18. if len(i) != 2:
  19. return False
  20. i = int(i)
  21. if n == 0 and i > 23:
  22. return False
  23. if i > 59:
  24. return False
  25. except:
  26. pass
  27. return True
  28. def ifdate(string):
  29. if len(string) != len("1997/07/30"):
  30. return False
  31. new_date_format = "%Y/%m/%d"
  32. try:
  33. datetime.datetime.strptime(string, new_date_format)
  34. ret = True
  35. except:
  36. ret = False
  37. return ret
  38. def if_days_a_week(string):
  39. try:
  40. string = int(string)
  41. if string <= 7 and string >= 1:
  42. return True
  43. else:
  44. return False
  45. except Exception as e:
  46. print("fail", e)
  47. return False
  48. def get_legacy(project_location):
  49. # This function will return analytics data about a project. This particular
  50. # function is desinged to read old, Blender-Organizer projects. It's a first
  51. # step of conversion. And used to display basic analitycs into the
  52. # project-manager.
  53. name_tmp = project_location[project_location.rfind("/")+1:]
  54. data = {
  55. "name" : name_tmp, # Name of the project (typed properly)
  56. "director" : "", # Name of the project's director.
  57. "status" : "", # Projects's comment / type
  58. "donework" : 0.0, # Percentage of Assets and Scenes done
  59. "fraction" : 0.0, # Project's completion percentage
  60. "checklist" : 0.0, # Project's main checklist percentage
  61. "startdate" : "0000/00/00", # Date of the start of the project
  62. "deadline" : "0000/00/00", # Date when project's deadline is
  63. "duration" : 0, # Amount in days between startdate and deadline
  64. "timepassed" : 0.0, # Percentage of how much time had passed
  65. "dayspassed" : 0, # Amount of days since the startdate
  66. "needed" : 0, # Needed % per day
  67. "star" : 0, # If star is reached
  68. "chr_factor" : 1, # Importance factor for Characters
  69. "veh_factor" : 1, # Importance factor for Vehicles
  70. "loc_factor" : 1, # Importance factor for Locations
  71. "obj_factor" : 1, # Importance factor for Objects (Other)
  72. "rnd_factor" : 4, # Importance factor for Scenes (Renders)
  73. "chr" : 0.0, # Percentage of Characters done
  74. "veh" : 0.0, # Percentage of Vehicles done
  75. "loc" : 0.0, # Percentage of Locations done
  76. "obj" : 0.0, # Percentage of Objects (Other) done
  77. "rnd" : 0.0, # Percentage of Scenes (Renders) done
  78. "dates" : {} # Per date, detailed data about the project
  79. }
  80. # For the future we will have to use some kind of network system. Where
  81. # different people contribute to one project. For example director, writer,
  82. # animator. For this I want to introduce a USERNAME thing. I will add them
  83. # later to tasks and schedulings.
  84. Username = settings.read("Username")
  85. if not Username:
  86. Username = "Blender-Organizer User"
  87. # Okay let's get the name, director and status from the old file. Funny that
  88. # it still survived from so far back. In the Organizer 1.0 you had to manually
  89. # type in the number of assets that had to be done in each category.
  90. # And so for this task was created a file called "project.data". Here is
  91. # an example :
  92. # Project :Moria's Race
  93. # Status :Short Action Driving Film
  94. # Director :J.Y.Amihud
  95. # Character:1
  96. # Locations:1
  97. # Objects :1
  98. # Vehicles :1
  99. # Scenes :4
  100. # So that's survived up to the last Blender-Organizer. But no longer the
  101. # last 5 lines were utilized. I was using it mainly for the first 3 things.
  102. # Tho from a version 4.85 of Blender-Organizer those last lines were used
  103. # to controll the influence factor. Basically the number stored will multiply
  104. # the number of times a given category is counted in the final percentage.
  105. # For example animating scenes should take more then half the time of the
  106. # project. More then 50% of the project then should be in the scenes. But
  107. # previous / primitive algorythm was giving each category an even 20%. So
  108. # of course I fixed it. LOL.
  109. # I think for VCStudio I gonna make a more unified file format. That will
  110. # unite all "project.data", "percentage_hystory.data", "history.data" and
  111. # "schedule.data".
  112. # I'm going to still have the main checklist separate tho. The checklist
  113. # format is quite good.
  114. projectdata = open(project_location+"/project.data")
  115. projectdata = projectdata.read()
  116. projectdata = projectdata.split("\n")
  117. for line in projectdata:
  118. if line.startswith("Project"):
  119. data["name"] = line[line.find(":")+1:]
  120. elif line.startswith("Status"):
  121. data["status"] = line[line.find(":")+1:]
  122. elif line.startswith("Director"):
  123. data["director"] = line[line.find(":")+1:]
  124. # Next up some integer conversions. So...
  125. elif line.startswith("Character"):
  126. try:
  127. data["chr_factor"] = int(line[line.find(":")+1:])
  128. except:
  129. data["chr_factor"] = 1
  130. elif line.startswith("Vehicles"):
  131. try:
  132. data["veh_factor"] = int(line[line.find(":")+1:])
  133. except:
  134. data["veh_factor"] = 1
  135. elif line.startswith("Locations"):
  136. try:
  137. data["loc_factor"] = int(line[line.find(":")+1:])
  138. except:
  139. data["loc_factor"] = 1
  140. elif line.startswith("Objects"):
  141. try:
  142. data["obj_factor"] = int(line[line.find(":")+1:])
  143. except:
  144. data["obj_factor"] = 1
  145. elif line.startswith("Scenes"):
  146. try:
  147. data["rnd_factor"] = int(line[line.find(":")+1:])
  148. except:
  149. data["rnd_factor"] = 1
  150. # Okay this first file was easy. Let's now parse the main checklist and
  151. # get 5 more values. Funny thing is that for the old checklists and new
  152. # checklists you can use the same function to read them. ( The main check-
  153. # list data ). But old checklists had something else in there that was
  154. # making them a bit different from VCStudio checklists. It's the STR and FIN
  155. # variables in the beginig.
  156. # See in the Organizer 1.0 there was no scheduling system as there was in
  157. # Blender-Orgaznier 4.9. The scheduling was done per asset and not per
  158. # action in the checklist. So STR and FIN were 2 dates between which you
  159. # should have had working on the asset.
  160. # But in the main checklists ("project.progress") those 2 survived to the
  161. # late Blender-Organizer as a project startdate and deadline.
  162. # One more rub that I have is that date format was 00/00/0000 and not the
  163. # way better one 0000/00/00 which makes sorting easy. So yeah...
  164. old_date_format = "%d/%m/%Y"
  165. new_date_format = "%Y/%m/%d"
  166. projectdata = open(project_location+"/project.progress")
  167. projectdata = projectdata.read()
  168. projectdata = projectdata.split("\n")
  169. startdate = datetime.datetime.today()
  170. deadline = datetime.datetime.today()
  171. try:
  172. for line in projectdata:
  173. if line.startswith("STR"):
  174. startdate = datetime.datetime.strptime(line[4:], old_date_format)
  175. data["startdate"] = datetime.datetime.strftime(startdate, new_date_format)
  176. elif line.startswith("FIN"):
  177. deadline = datetime.datetime.strptime(line[4:], old_date_format)
  178. data["deadline"] = datetime.datetime.strftime(deadline, new_date_format)
  179. except:
  180. data["startdate"] = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
  181. data["deadline"] = datetime.datetime.strftime(datetime.datetime.today()+datetime.timedelta(days=30), new_date_format)
  182. # So we've go the dates. Let's calculate time perventage I guess.
  183. delta = deadline - startdate
  184. data["duration"] = int(delta.days)
  185. delta = datetime.datetime.today() - startdate
  186. data["dayspassed"] = int(delta.days)
  187. data["timepassed"] = data["dayspassed"] / data["duration"]
  188. if data["timepassed"] > 1.0:
  189. data["timepassed"] = 1.0
  190. # Now let's lauch the main checklist and get the data from there. I mean
  191. # the percentage. Btw It's just a hard thing. That I needed a separate
  192. # function for it.
  193. try:
  194. projectdata = checklist.get_list(project_location+"/project.progress")
  195. data["checklist"] = projectdata["fraction"]
  196. except:
  197. pass
  198. # NEXT THING. As I love to type it into place where people read me while I'm
  199. # working. We've got data from 2 files. Now we need to get data from ALL the
  200. # project.
  201. # First we going to get data about the assets. Because it's relativelly easy
  202. # compared to the story. For which you need to parce a crazy complicated .bos
  203. # file. Which is a complex database in it's own right.
  204. # So let's go and quickly get data about the assets.
  205. asstfols = ["chr", "veh", "loc", "obj"]
  206. astlist = []
  207. for n , f in enumerate(asstfols):
  208. flist = []
  209. if len(os.listdir(project_location+"/dev/"+f)) > 0:
  210. for asset in os.listdir(project_location+"/dev/"+f):
  211. if asset+".blend" in os.listdir(project_location+"/ast/"+f):
  212. flist.append(1.0)
  213. else:
  214. try:
  215. fcheck = checklist.get_list(project_location+"/dev/"+f+"/"+asset+"/asset.progress")
  216. flist.append(fcheck["fraction"])
  217. except:
  218. flist.append(0.0)
  219. # The multiplication thing that I was talking about earlier.
  220. multiply = data[f+"_factor"]
  221. for m in range(multiply):
  222. astlist.append(sum(flist)/len(flist))
  223. data[f] = sum(flist)/len(flist)
  224. # For the next step I need to have the story parsed and read. But it's going
  225. # to be so hard. That I will need to write a separate function for it.
  226. data["rnd"] = story.get_legacy(project_location)["fraction"]
  227. # After all of it we need to get the final project percentage.
  228. multiply = data["rnd_factor"]
  229. for m in range(multiply):
  230. astlist.append(data["rnd"])
  231. try:
  232. data["donework"] = sum(astlist) / len(astlist)
  233. except:
  234. data["donework"] = 0.0
  235. data["fraction"] = (data["donework"] + data["checklist"]) / 2
  236. # Next section of this data gathering will be about history, scheduling and
  237. # similar things. I gonna create a dictionary of dates. And input data about
  238. # those dates into the dates.
  239. # One thing that I have to make sure about is that some dates could be out-
  240. # side of the range between startdate and deadline. So I have to read the
  241. # files and add them dynamically.
  242. # Let's start by reading scheduling.
  243. sdata = open(project_location+"/schedule.data")
  244. sdata = sdata.read()
  245. sdata = sdata.split("\n")
  246. # Good that schedules already have the right date format. Altho I think of
  247. # actually using a datetime object as a date. Or is it too much? Let me think
  248. # I do think loudly. And sinse I have nobody to talk to I will type it here.
  249. # If you couldn't tell already. Let's write it as a string for now. Because
  250. # it's easier to work wit strings. Maybe in future I will write it as a
  251. # datetime object. If i'll find benefit to this.
  252. for date in sdata:
  253. # Let's separate the string into 3 components. Basically scheduling is
  254. # list of tasks in various checklists through out the project. Example:
  255. # 2020/11/30 /dev/obj/Morias_Bike/asset.progress Rigging=:> Make Bones
  256. # 2021/05/01 project.progress Marketing=:> Release
  257. # The first entry is a date formatted yyyy/mm/dd. Then comes path of the
  258. # checklist location. Relative to the project_location. Then a url type
  259. # thing for with in the checklist. Separating entries with "=:>"
  260. # Also I have to note that in the old Blender-Organizer date 1997/07/30
  261. # which is my birthday, will make the task be everyday. It will have it's
  262. # own Green color. Usually when you make a new project there are a list
  263. # of task created for this date.
  264. if date:
  265. d = date[:date.find(" ")] # Date
  266. f = date[date.find(" ")+1:date.replace(" ", ".", 1).find(" ")] # File
  267. t = date[date.replace(" ", ".", 1).find(" ")+1:].split("=:>") # Task
  268. if d not in data["dates"]:
  269. data["dates"][d] = {}
  270. # Now in order to decide how to input this data into the data["dates"]
  271. # we need to look at history at first. Because first of all it has
  272. # similar syntax. But on the other hand it's a bit more complex.
  273. # 2020/07/05 03:06:34 /dev/chr/Civilian_Child_2/asset.progress Rigging=:> Face Rig=:> Mouth area [V]
  274. # 2020/07/05 03:06:35 /dev/chr/Civilian_Child_2/asset.progress Rigging=:> Ready to make AST [V]
  275. # 2020/07/05 03:06:37 /dev/chr/Civilian_Child_2/Civilian_Child_2.blend [Openned]
  276. # 2020/07/05 03:07:56 /dev/chr/Civilian_Child_2/autolink.data [Updated]
  277. # 2020/07/05 03:08:08 pln/main.bos [Edited]
  278. # 2020/07/05 03:08:13 /rnd/Scene_2a5jfq6126fe/1/Cc1_IK_Test.blend [Openned]
  279. # 2020/07/05 03:08:25 /rnd/Scene_2a5jfq6126fe/1/Cc1_IK_Test.blend [Linked]
  280. # 2020/07/05 03:08:25 /rnd/Scene_2a5jfq6126fe/1/Cc1_IK_Test.blend [Openned]
  281. # Also history has a feature missing in the schedules. But there is
  282. # a rub. Scheduling you can rearange by moving them arround. And you
  283. # can simply move a task to a different day if you so desire.
  284. # In the history apart from the date you also have time up to the second
  285. # which is a bit more precise in terms of when exactly you made a
  286. # given task. And I would like scheduling to have the same type of
  287. # precision. Maybe even introduce a notification system if a certain
  288. # task is due. But it makes the moving of tasks highly complicated
  289. # to visualize.
  290. # Maybe a timeline could be a cool idea. Like in the VSE of blender.
  291. # a place where you could put specific points on a timeline. And time
  292. # to the next point will be hte length of the part.
  293. # You could move the dots so to change timing of parts. And maybe
  294. # select them to type in precise values. Could work.
  295. # Let's separate the schedule to 3 types. Asset, Scene, Main.
  296. # If it's a scene we are talking about. IK it's a bit shitty. Because
  297. # I will need to parse this as well. But who cares.
  298. if f.startswith("/rnd"):
  299. ty = "scenes"
  300. url = f[:f.rfind("/")].replace("/rnd", "", 1)
  301. url = url[:url.rfind("/")]
  302. fn = f.replace("/rnd", "", 1).replace(url, "")
  303. elif f.startswith("/dev"):
  304. ty = "assets"
  305. url = f[:f.rfind("/")].replace("/dev", "", 1)
  306. fn = f.replace("/dev", "", 1).replace(url, "")
  307. else:
  308. ty = "files"
  309. url = ""
  310. fn = f
  311. if not ty in data["dates"][d]:
  312. data["dates"][d][ty] = {}
  313. if not url in data["dates"][d][ty]:
  314. data["dates"][d][ty][url] = []
  315. data["dates"][d][ty][url].append([
  316. "00:00:00",
  317. "schedule",
  318. fn,
  319. t,
  320. Username
  321. ])
  322. # Okay I don't really know what exactly did I just do. But it's seems like
  323. # a good starting point. Up to a working verion of VCStudio i can edit and
  324. # change these all I want. Okay I guess it's time to parse another, way
  325. # harder file. The history. LOL.
  326. # First let's deal with the persantage history file. It's kind of a funny
  327. # one as well. The funny part is that it has no consistancy at all. I will
  328. # need to convert it as well. It has a stupid %y-%m-%d format. Which is WTF.
  329. history_percentage_format = "%y-%m-%d"
  330. hdata = open(project_location+"/percentage_hystory.data") # IK spelling LOL
  331. hdata = hdata.read()
  332. hdata = hdata.split("\n")
  333. for date in hdata:
  334. if date.startswith("DATE"):
  335. # An example of the formatting is actually quite amazing. It's going
  336. # to be realively simple. Example:
  337. # DATE 20-06-26 42.64%
  338. # As you can see it's the word DATE then that date then the fraction
  339. # as a percantage. Separated by a spacebar.
  340. try:
  341. t, d, f = date.split(" ")
  342. # Converting the d into a normal date
  343. d = datetime.datetime.strptime(d, history_percentage_format)
  344. d = datetime.datetime.strftime(d, new_date_format)
  345. # Converting the f into a fraction
  346. f = float(f.replace("%", "")) / 100
  347. except:
  348. continue
  349. # I just don't want to deal with poeple who are editing the file
  350. # manually and then say I did something wrong that the program crashed
  351. if d not in data["dates"]:
  352. data["dates"][d] = {}
  353. data["dates"][d]["fractions"] = {
  354. "project":f, # The fraction we recieved from the file
  355. "checklist":0.0, # Place holder for checklist fraction
  356. "chr":0.0, # \
  357. "veh":0.0, # |
  358. "loc":0.0, # > - Plaseholders for categories
  359. "obj":0.0, # |
  360. "rnd":0.0 # /
  361. }
  362. # Okay this file was parsed. Which was relativelly simple. NOW. Let's
  363. # do something a bit harder. Or is it as simple as the other? IDK
  364. # History file. Example was a bit earlier.
  365. hdata = open(project_location+"/history.data")
  366. hdata = hdata.read()
  367. hdata = hdata.split("\n")
  368. for line in hdata:
  369. if not line:
  370. continue
  371. # So basically every line is quite simple from the left side. But becomes
  372. # quite complex on the right side. So let's get the left side things
  373. date = line[:line.find(" ")]
  374. time = line[line.find(" ")+1:line.replace(" ", ".", 1).find(" ")]
  375. path = line[line.replace(" ", ".", 1).find(" ")+1:line.replace(" ", ".", 2).find(" ")]
  376. done = line[line.replace(" ", ".", 2).find(" ")+1:]
  377. # I made a mistake allowing paths to have spaces in them. And I need to
  378. # make sure we have the whole path and not only up to a point.
  379. while done[0] != "[" and ".progress" not in path and "[" in done:
  380. transporting = done[:done.find(" ")]
  381. path = path+" "+transporting
  382. done = done.replace(transporting+" ", "")
  383. # "date" will be our standard date. yyyy/mm/dd. Then "time" is out time
  384. # from the current day. 24 hours system. hh:mm:ss. "path" is the file to
  385. # which the change was done. We will need to parse this one. Because it
  386. # contains the full path to let's say a blend file. While we want to
  387. # know which asset for example was accessed. Then "done" is the rest of
  388. # the string. Basically it tells what was actually done with a given file.
  389. # We will need to parse this one as well somehow. And it scares me a lot
  390. # One thing that we can use it for is restoring the old scheduling list.
  391. # Becuase in lazy attempt to make user know that a given task is finished
  392. # I would just delete the task from the schedule file entirely. But
  393. # in the history file you can find instances of [scheduled] with the full
  394. # path of a task and the date to which it was scheduled. So yeah.
  395. # Let's get to work.
  396. f = path # Copied from previous
  397. if f.startswith("/rnd"):
  398. ty = "scenes"
  399. url = f[:f.rfind("/")].replace("/rnd", "", 1)
  400. url = url[:url.rfind("/")]
  401. fn = f.replace("/rnd", "", 1).replace(url, "")
  402. elif f.startswith("/dev"):
  403. ty = "assets"
  404. url = f[:f.rfind("/")].replace("/dev", "", 1)
  405. fn = f.replace("/dev", "", 1).replace(url, "")
  406. elif f.startswith("/ast") and ".blend" in f:
  407. ty = "assets"
  408. url = f[:f.rfind(".")].replace("/ast", "", 1)
  409. fn = "[asset_blend]"
  410. else:
  411. ty = "files"
  412. url = ""
  413. fn = f
  414. # Now in order to parse the "done" variable. We need to define functions
  415. # that we are not looking for. Simple (Trivial) Operations.
  416. simple_operations = [
  417. "[Edited]",
  418. "[Openned]",
  419. "[Linked]",
  420. "[Edited]",
  421. "[Updated]",
  422. "[Added]",
  423. "[Added Asset]"
  424. ]
  425. # Basically if "done" in on that list. It's something to do with
  426. # checklists. And then we have either [V], [ ] or [Scheduled]
  427. if done not in simple_operations:
  428. if "[Scheduled]" in done:
  429. # These are the missing scheduling data I was talking about.
  430. # Let's parse this data from the back. The one on the back
  431. # should be the DATE. Then spacebar. Then the [Scheduled] then
  432. # another spacebar then the task.
  433. missingdate = done[done.rfind(" ")+1:]
  434. missingtask = done[:done.rfind(" [Scheduled]")].split("=:>")
  435. # Let's add them into the data. I don't what it will be. Let's
  436. # see.
  437. if not missingdate in data["dates"]:
  438. data["dates"][missingdate] = {}
  439. if not ty in data["dates"][missingdate]:
  440. data["dates"][missingdate][ty] = {}
  441. if not url in data["dates"][missingdate][ty]:
  442. data["dates"][missingdate][ty][url] = []
  443. data["dates"][missingdate][ty][url].append([
  444. "00:00:00",
  445. "schedule",
  446. fn,
  447. missingtask,
  448. Username
  449. ])
  450. # Or else it's a checklist whether been checked or unchecked
  451. else:
  452. if "[V]" in done:
  453. done = done.replace(" [V]", "").split("=:>")
  454. check = "[Checked]"
  455. else:
  456. done = done.replace(" [ ]", "").split("=:>")
  457. check = "[Un-Checked]"
  458. # Putting the thing into the data
  459. if not date in data["dates"]:
  460. data["dates"][date] = {}
  461. if not ty in data["dates"][date]:
  462. data["dates"][date][ty] = {}
  463. if not url in data["dates"][date][ty]:
  464. data["dates"][date][ty][url] = []
  465. data["dates"][date][ty][url].append([
  466. time,
  467. "history",
  468. fn,
  469. check,
  470. done,
  471. Username
  472. ])
  473. #print(data["dates"][date][ty][url])
  474. # Now let's add all the others.
  475. else:
  476. if not date in data["dates"]:
  477. data["dates"][date] = {}
  478. if not ty in data["dates"][date]:
  479. data["dates"][date][ty] = {}
  480. if not url in data["dates"][date][ty]:
  481. data["dates"][date][ty][url] = []
  482. data["dates"][date][ty][url].append([
  483. time,
  484. "history",
  485. fn,
  486. done,
  487. Username
  488. ])
  489. #print(ty, url, fn, done)
  490. #for i in sorted(data["dates"]):
  491. # print(i)
  492. # print()
  493. # print(data["dates"][i])
  494. # print()
  495. # print()
  496. #data_save(project_location, data)
  497. return data
  498. def save(project, data):
  499. # This file will save analitycs data.
  500. try:
  501. os.mkdir(project+'/set/')
  502. except:
  503. pass
  504. with open(project+'/set/analytics.json', 'w') as fp:
  505. json.dump(data, fp, sort_keys=True, indent=4)
  506. def load(project_location):
  507. # This is a simple load analytics funtion.
  508. name_tmp = project_location[project_location.rfind("/")+1:]
  509. data = {
  510. "name" : name_tmp, # Name of the project (typed properly)
  511. "director" : "", # Name of the project's director.
  512. "status" : "", # Projects's comment / type
  513. "donework" : 0.0, # Percentage of Assets and Scenes done
  514. "fraction" : 0.0, # Project's completion percentage
  515. "checklist" : 0.0, # Project's main checklist percentage
  516. "startdate" : "0000/00/00", # Date of the start of the project
  517. "deadline" : "0000/00/00", # Date when project's deadline is
  518. "duration" : 0, # Amount in days between startdate and deadline
  519. "timepassed" : 0.0, # Percentage of how much time had passed
  520. "dayspassed" : 0, # Amount of days since the startdate
  521. "chr_factor" : 1, # Importance factor for Characters
  522. "needed" : 0, # Needed % to do today
  523. "star" : 0, # Did you get needed
  524. "veh_factor" : 1, # Importance factor for Vehicles
  525. "loc_factor" : 1, # Importance factor for Locations
  526. "obj_factor" : 1, # Importance factor for Objects (Other)
  527. "rnd_factor" : 4, # Importance factor for Scenes (Renders)
  528. "chr" : 0.0, # Percentage of Characters done
  529. "veh" : 0.0, # Percentage of Vehicles done
  530. "loc" : 0.0, # Percentage of Locations done
  531. "obj" : 0.0, # Percentage of Objects (Other) done
  532. "rnd" : 0.0, # Percentage of Scenes (Renders) done
  533. "dates" : {} # Per date, detailed data about the project
  534. }
  535. try:
  536. with open(project_location+'/set/analytics.json') as json_file:
  537. data = json.load(json_file)
  538. except:
  539. pass
  540. try:
  541. projectdata = checklist.get_list(project_location+"/set/project.progress")
  542. data["checklist"] = projectdata["fraction"]
  543. except:
  544. make = open(project_location+"/set/project.progress", "w")
  545. make.write("[ ] Story\n")
  546. make.write("[ ] "+talk.text("chr")+"\n")
  547. make.write("[ ] "+talk.text("veh")+"\n")
  548. make.write("[ ] "+talk.text("loc")+"\n")
  549. make.write("[ ] "+talk.text("obj")+"\n")
  550. make.write("[ ] Animation\n")
  551. make.write("[ ] Rendering")
  552. make.close()
  553. # Let's see if dates are fine. Or if they are even dates.
  554. new_date_format = "%Y/%m/%d"
  555. if not ifdate(data["startdate"]) or not ifdate(data["deadline"]):
  556. data["startdate"] = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
  557. data["deadline"] = datetime.datetime.strftime(datetime.datetime.today()+datetime.timedelta(days=30), new_date_format)
  558. # So we've go the dates. Let's calculate time perventage I guess.
  559. startdate = datetime.datetime.strptime(data["startdate"], new_date_format)
  560. deadline = datetime.datetime.strptime(data["deadline"] , new_date_format)
  561. delta = deadline - startdate
  562. data["duration"] = int(delta.days)
  563. delta = datetime.datetime.today() - startdate
  564. data["dayspassed"] = int(delta.days)
  565. data["timepassed"] = data["dayspassed"] / data["duration"]
  566. if data["timepassed"] > 1.0:
  567. data["timepassed"] = 1.0
  568. # LET's FILTER THE SCHEDULES
  569. data["dates"] = schedule.filter(project_location, data["dates"])
  570. # NEXT THING. As I love to type it into place where people read me while I'm
  571. # working. We've got data from 2 files. Now we need to get data from ALL the
  572. # project.
  573. # First we going to get data about the assets. Because it's relativelly easy
  574. # compared to the story. For which you need to parce a crazy complicated .bos
  575. # file. Which is a complex database in it's own right.
  576. # So let's go and quickly get data about the assets.
  577. asstfols = ["chr", "veh", "loc", "obj"]
  578. astlist = []
  579. for n , f in enumerate(asstfols):
  580. flist = []
  581. if len(os.listdir(project_location+"/dev/"+f)) > 0:
  582. for asset in os.listdir(project_location+"/dev/"+f):
  583. if asset+".blend" in os.listdir(project_location+"/ast/"+f):
  584. flist.append(1.0)
  585. else:
  586. try:
  587. fcheck = checklist.get_list(project_location+"/dev/"+f+"/"+asset+"/asset.progress")
  588. flist.append(fcheck["fraction"])
  589. except:
  590. flist.append(0.0)
  591. # The multiplication thing that I was talking about earlier.
  592. multiply = data[f+"_factor"]
  593. for m in range(multiply):
  594. astlist.append(sum(flist)/len(flist))
  595. data[f] = sum(flist)/len(flist)
  596. # For the next step I need to have the story parsed and read. But it's going
  597. # to be so hard. That I will need to write a separate function for it.
  598. data["rnd"] = story.load(project_location)["fraction"]
  599. # After all of it we need to get the final project percentage.
  600. multiply = data["rnd_factor"]
  601. for m in range(multiply):
  602. astlist.append(data["rnd"])
  603. try:
  604. data["donework"] = sum(astlist) / len(astlist)
  605. except:
  606. data["donework"] = 0.0
  607. # I decided to remove the main checklist from the main fraction. This way the fraction
  608. # reflects better the overall project. I will keep the old code in a comment for a while.
  609. data["fraction"] = data["donework"] #(data["donework"] + data["checklist"]) / 2
  610. # Let's record it for today.
  611. today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
  612. if today not in data["dates"]:
  613. data["dates"][today] = {}
  614. data["dates"][today]["fractions"] = {
  615. "project":data["fraction"],
  616. "checklist":data["checklist"],
  617. "chr":data["chr"],
  618. "veh":data["veh"],
  619. "loc":data["loc"],
  620. "obj":data["obj"],
  621. "rnd":data["rnd"]
  622. }
  623. # Needed
  624. try:
  625. alldates = list(reversed(data["dates"].keys()))
  626. prevdate = alldates[alldates.index(today)+1]
  627. prev_frac = data["dates"][prevdate].get("fractions", {}).get("project", data["fraction"])
  628. data["needed"] = ( 1 - prev_frac ) / (( data["duration"] - data["dayspassed"] )/7*data.get("days_a_week", 7))
  629. except:
  630. data["needed"] = 0
  631. prev_star = data.get("star", 0)
  632. try:
  633. alldates = list(reversed(data["dates"].keys()))
  634. prevdate = alldates[alldates.index(today)+1]
  635. prev_frac = data["dates"][prevdate].get("fractions", {}).get("project", data["fraction"])
  636. data["star"] = (data["fraction"] - prev_frac) / data["needed"]
  637. except:
  638. data["star"] = 0
  639. if data["star"] >= 2 and not prev_star >= 2:
  640. talk.alert("⭐ Today's Requirement is Finished!")
  641. return data