diff --git a/studio/analytics.py b/studio/analytics.py new file mode 100644 index 0000000..98300e0 --- /dev/null +++ b/studio/analytics.py @@ -0,0 +1,244 @@ +# THIS FILE IS A PART OF VCStudio +# PYTHON 3 + +import os +import datetime + +#from studio import checklist +import checklist + +def get_legacy(project_location): + + # This function will return analytics data about a project. This particular + # function is desinged to read old, Blender-Organizer projects. It's a first + # step of conversion. And used to display basic analitycs into the + # project-manager. + + data = { + "name" : "", # Name of the project (typed properly) + "director" : "", # Name of the project's director. + "status" : "", # Projects's comment / type + "donework" : 0.0, # Percentage of Assets and Scenes done + "fraction" : 0.0, # Project's completion percentage + "checklist" : 0.0, # Project's main checklist percentage + "startdate" : "0000/00/00", # Date of the start of the project + "deadline" : "0000/00/00", # Date when project's deadline is + "duration" : 0, # Amount in days between startdate and deadline + "timepassed" : 0.0, # Percentage of how much time had passed + "dayspassed" : 0, # Amount of days since the startdate + "chr_factor" : 1, # Importance factor for Characters + "veh_factor" : 1, # Importance factor for Vehicles + "loc_factor" : 1, # Importance factor for Locations + "obj_factor" : 1, # Importance factor for Objects (Other) + "rnd_factor" : 1, # Importance factor for Scenes (Renders) + "chr" : 0.0, # Percentage of Characters done + "veh" : 0.0, # Percentage of Vehicles done + "loc" : 0.0, # Percentage of Locations done + "obj" : 0.0, # Percentage of Objects (Other) done + "rnd" : 0.0, # Percentage of Scenes (Renders) done + "dates" : {} # Per date, detailed data about the project + } + + # Okay let's get the name, director and status from the old file. Funny that + # it still survived from so far back. In the Organizer 1.0 you had to manually + # type in the number of assets that had to be done in each category. + + # And so for this task was created a file called "project.data". Here is + # an example : + + # Project :Moria's Race + # Status :Short Action Driving Film + # Director :J.Y.Amihud + # Character:1 + # Locations:1 + # Objects :1 + # Vehicles :1 + # Scenes :4 + + # So that's survived up to the last Blender-Organizer. But no longer the + # last 5 lines were utilized. I was using it mainly for the first 3 things. + + # Tho from a version 4.85 of Blender-Organizer those last lines were used + # to controll the influence factor. Basically the number stored will multiply + # the number of times a given category is counted in the final percentage. + + # For example animating scenes should take more then half the time of the + # project. More then 50% of the project then should be in the scenes. But + # previous / primitive algorythm was giving each category an even 20%. So + # of course I fixed it. LOL. + + # I think for VCStudio I gonna make a more unified file format. That will + # unite all "project.data", "percentage_hystory.data", "history.data" and + # "schedule.data". + + # I'm going to still have the main checklist separate tho. The checklist + # format is quite good. + + projectdata = open(project_location+"/project.data") + projectdata = projectdata.read() + projectdata = projectdata.split("\n") + + for line in projectdata: + + if line.startswith("Project"): + data["name"] = line[line.find(":")+1:] + + elif line.startswith("Status"): + data["status"] = line[line.find(":")+1:] + + elif line.startswith("Director"): + data["director"] = line[line.find(":")+1:] + + # Next up some integer conversions. So... + + elif line.startswith("Character"): + try: + data["chr_factor"] = int(line[line.find(":")+1:]) + except: + data["chr_factor"] = 1 + + + elif line.startswith("Vehicles"): + try: + data["veh_factor"] = int(line[line.find(":")+1:]) + except: + data["veh_factor"] = 1 + + elif line.startswith("Locations"): + try: + data["loc_factor"] = int(line[line.find(":")+1:]) + except: + data["loc_factor"] = 1 + + elif line.startswith("Objects"): + try: + data["obj_factor"] = int(line[line.find(":")+1:]) + except: + data["obj_factor"] = 1 + + elif line.startswith("Scenes"): + try: + data["rnd_factor"] = int(line[line.find(":")+1:]) + except: + data["rnd_factor"] = 1 + + # Okay this first file was easy. Let's now parse the main checklist and + # get 5 more values. Funny thing is that for the old checklists and new + # checklists you can use the same function to read them. ( The main check- + # list data ). But old checklists had something else in there that was + # making them a bit different from VCStudio checklists. It's the STR and FIN + # variables in the beginig. + + # See in the Organizer 1.0 there was no scheduling system as there was in + # Blender-Orgaznier 4.9. The scheduling was done per asset and not per + # action in the checklist. So STR and FIN were 2 dates between which you + # should have had working on the asset. + + # But in the main checklists ("project.progress") those 2 survived to the + # late Blender-Organizer as a project startdate and deadline. + + # One more rub that I have is that date format was 00/00/0000 and not the + # way better one 0000/00/00 which makes sorting easy. So yeah... + + old_date_format = "%d/%m/%Y" + new_date_format = "%Y/%m/%d" + + projectdata = open(project_location+"/project.progress") + projectdata = projectdata.read() + projectdata = projectdata.split("\n") + + startdate = datetime.datetime.today() + deadline = datetime.datetime.today() + + for line in projectdata: + if line.startswith("STR"): + startdate = datetime.datetime.strptime(line[4:], old_date_format) + data["startdate"] = datetime.datetime.strftime(startdate, new_date_format) + + elif line.startswith("FIN"): + deadline = datetime.datetime.strptime(line[4:], old_date_format) + data["deadline"] = datetime.datetime.strftime(deadline, new_date_format) + + # So we've go the dates. Let's calculate time perventage I guess. + delta = deadline - startdate + data["duration"] = int(delta.days) + + delta = datetime.datetime.today() - startdate + data["dayspassed"] = int(delta.days) + + data["timepassed"] = data["dayspassed"] / data["duration"] + if data["timepassed"] > 1.0: + data["timepassed"] = 1.0 + + # Now let's lauch the main checklist and get the data from there. I mean + # the percentage. Btw It's just a hard thing. That I needed a separate + # function for it. + + projectdata = checklist.get_list(project_location+"/project.progress") + data["checklist"] = projectdata["fraction"] + + # NEXT THING. As I love to type it into place where people read me while I'm + # working. We've got data from 2 files. Now we need to get data from ALL the + # project. + + # First we going to get data about the assets. Because it's relativelly easy + # compared to the story. For which you need to parce a crazy complicated .bos + # file. Which is a complex database in it's own right. + + # So let's go and quickly get data about the assets. + + asstfols = ["chr", "veh", "loc", "obj"] + astlist = [] + + for n , f in enumerate(asstfols): + + flist = [] + + if len(os.listdir(project_location+"/dev/"+f)) > 0: + for asset in os.listdir(project_location+"/dev/"+f): + + if asset+".blend" in os.listdir(project_location+"/ast/"+f): + flist.append(1.0) + + else: + try: + fcheck = checklist.get_list(project_location+"/dev/"+f+"/"+asset+"/asset.progress") + flist.append(fcheck["fraction"]) + except: + flist.append(0.0) + + # The multiplication thing that I was talking about earlier. + + multiply = data[f+"_factor"] + for m in range(multiply): + astlist.append(sum(flist)/len(flist)) + + data[f] = sum(flist)/len(flist) + + + # For the next step I need to have the story parsed and read. But it's going + # to be so hard. That I will need to write a separate function for it. + + data["rnd"] = 0.0 + + # After all of it we need to get the final project percentage. + multiply = data["rnd_factor"] + for m in range(multiply): + astlist.append(data["rnd"]) + + try: + data["donework"] = sum(astlist) / len(astlist) + except: + data["donework"] = 0.0 + data["fraction"] = (data["donework"] + data["checklist"]) / 2 + + # Next section of this data gathering will be about history, scheduling and + # similar things. I gonna create a dictionary of dates. And input data about + # those dates into the dates. + + + return data + +this = get_legacy("/home/vcs/Projects/Films/Shorts/MoriasRace") +for i in this: + print(i, " : " ,this[i]) diff --git a/studio/checklist.py b/studio/checklist.py new file mode 100644 index 0000000..dfdcd64 --- /dev/null +++ b/studio/checklist.py @@ -0,0 +1,163 @@ +# THIS FILE IS A PART OF VCStudio +# PYTHON 3 + +import os + +def get_list(filepath): + + # This fucntion converts text documents. (.progress) into a more machine + # friendly recursive dict. + + # In the original organizer evaluation of the checklist was done separatelly + # in a different function. Which made it very messy for recursive stuff. + # I will attemp to combine it. And make it more clean as a result. + + checklist = { + "fraction":0.0, # The percentage of a checklist. From 0.0 to 1.0 + "string":filepath, + "editing":False,# Whethere the string is during editing. UI. + "open":True, # Whether to draw the suptasks. UI. + "subtasks":[] # List of subtastks. (In the same format as the checklist) + } + + data = open(filepath) + data = data.read() + data = data.split("\n") + + # Let's filter out all the comments. Lines starting with #. For some reason + # in the old organizer. I just thought it wasn't important. LOL. And just + # started reading from the 10th line. + + # Here is an example of the first 9 lines. + + # 1 #### Blender orgainizer checklist format + # 2 #### INDINTATION (4 SPACES LONG) + # 3 #### STR means Start date of the ASSET + # 4 #### FIN means Finish deadline of the asset + # 5 #### [ ] means that task is on list + # 6 #### [V] means that tast is finished + # 7 #### DO NOT USE EMPTY LINES + # 8 STR 24/02/2020 + # 9 FIN 01/05/2021 + + # You can see a trace from a very long time ago. From the first versions + # of the blender organizer. The STR and FIN values. Which are start and + # deadline of the project. + + # I guess we need to filter it out a bit differently. Checking whether a + # given line starts with a [ or with a number of spaces and [. This will + # make the file a little more open to editing by hand. Without too much + # worrying that something will break. + + cleandata = [] + + for line in data: + + # So not to mangle the line. + tmp = line + while tmp.startswith(" "): + tmp = tmp[1:] + + # Checking + if tmp.startswith("[ ]") or tmp.startswith("[V]"): + cleandata.append(line) + + # Now since we have the cleandata. We can try to parse it somehow into a + # checklist thing. For this we need a reqursion. I gonna use a method from + # the blender-organizer. By running the function with in itself. It's not + # very wise. I know. In python3 it will give you no more then 996 recursions + # untill it will declare an error. BUT. It's 996 layers of a subtaks. + # For now I don't see a need in so many subtasks. Let's think of layers of + # subtasks as of memory. This laptop has only 8 GB of RAM. I can't put more + # data into it even if I wanted. + + def convert(part, indent=0): + # If a thing have subtasks it should not even be a checkbox. So trying + # To evaluate it's fraction by whether it's a [ ] or [V] shoun't be + # done. + + subtask = [] + + for num, line in enumerate(part): + + # Let's get the NEXT line. I'm not kidding. We gonna work with the + # next line to see whether to count this lines [V] + + + if line[indent:].startswith("["): + + thisline = { + "fraction":0.0, + "string":line[line.find("]")+2:], + "editing":False, + "open":False, + "subtasks":[] + } + + try: + nextline = part[num+1] + except: + nextline = "" + + if not line[line.find("]")+1] == ".": + thisline["open"] = True + + if nextline.find("[")-1 <= indent: + if line[indent:].startswith("[V]"): + thisline["fraction"] = 1.0 + + else: + subpart = [] + subdent = indent + for n, l in enumerate(part[num+1:]): + if n == 0: + subdent = l.find("[") + + if l.find("[")-1 <= indent: + break + + else: + subpart.append(l) + + #print(subpart) + thisline["subtasks"] = convert(subpart, subdent) + + fracs = [] + for task in thisline["subtasks"]: + if not task["string"].startswith("#"): + fracs.append(task["fraction"]) + + try: + thisline["fraction"] = sum(fracs) / len(fracs) + except: + thisline["fraction"] = 0.0 + + # Sometime it was showing 99% when infect it's 100% + if thisline["fraction"] == 0.9999: + thisline["fraction"] = 1.0 + + + subtask.append(thisline) + + return subtask + + + checklist["subtasks"] = convert(cleandata) + fracs = [] + for task in checklist["subtasks"]: + if not task["string"].startswith("#"): + fracs.append(task["fraction"]) + + try: + checklist["fraction"] = sum(fracs) / len(fracs) + except: + checklist["fraction"] = 0.0 + + # Sometime it was showing 99% when infect it's 100% + if checklist["fraction"] == 0.9999: + checklist["fraction"] = 1.0 + + + return checklist + +