From e85be921deaf3937d1410892153fcad7453a8e61 Mon Sep 17 00:00:00 2001
From: "Jeison Yehuda Amihud (Blender Dumbass)" <blenderdumbass@gmail.com>
Date: Mon, 30 Nov 2020 13:19:22 +0000
Subject: [PATCH] History / Scheduling Parsing

Now it can read and even save files from the history / scheduling form the old Blender-Organizer.
In the end you can see some commented file writing things. You can plug your own location there and test this feature out. It gives hell of a clean file.
I know I'll have to be able to parse the new file as well. Yeah.
---
 studio/analytics.py | 460 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 460 insertions(+)

diff --git a/studio/analytics.py b/studio/analytics.py
index c246168..cdc46ca 100644
--- a/studio/analytics.py
+++ b/studio/analytics.py
@@ -238,6 +238,466 @@ def get_legacy(project_location):
     # similar things. I gonna create a dictionary of dates. And input data about
     # those dates into the dates. 
     
+    # One thing that I have to make sure about is that some dates could be out-
+    # side of the range between startdate and deadline. So I have to read the 
+    # files and add them dynamically. 
     
+    # Let's start by reading scheduling. 
+    
+    sdata = open(project_location+"/schedule.data")
+    sdata = sdata.read()
+    sdata = sdata.split("\n")
+    
+    # Good that schedules already have the right date format. Altho I think of
+    # actually using a datetime object as a date. Or is it too much? Let me think
+    # I do think loudly. And sinse I have nobody to talk to I will type it here.
+    # If you couldn't tell already. Let's write it as a string for now. Because
+    # it's easier to work wit strings. Maybe in future I will write it as a
+    # datetime object. If i'll find benefit to this. 
+    
+    for date in sdata:
+        
+        # Let's separate the string into 3 components. Basically scheduling is
+        # list of tasks in various checklists through out the project. Example:
+        
+        # 2020/11/30 /dev/obj/Morias_Bike/asset.progress  Rigging=:> Make Bones
+        # 2021/05/01 project.progress  Marketing=:> Release
+        
+        # The first entry is a date formatted yyyy/mm/dd. Then comes path of the
+        # checklist location. Relative to the project_location. Then a url type
+        # thing for with in the checklist. Separating entries with "=:>"
+        
+        # Also I have to note that in the old Blender-Organizer date 1997/07/30
+        # which is my birthday, will make the task be everyday. It will have it's
+        # own Green color. Usually when you make a new project there are a list
+        # of task created for this date.
+        
+        if date:
+            d = date[:date.find(" ")]                                       # Date
+            f = date[date.find(" ")+1:date.replace(" ", ".", 1).find(" ")]  # File
+            t = date[date.replace(" ", ".", 1).find(" ")+1:].split("=:>")   # Task
+            
+            if d not in data["dates"]:
+                data["dates"][d] = {}
+            
+            # Now in order to decide how to input this data into the data["dates"]
+            # we need to look at history at first. Because first of all it has
+            # similar syntax. But on the other hand it's a bit more complex.
+            
+            #    2020/07/05 03:06:34 /dev/chr/Civilian_Child_2/asset.progress  Rigging=:> Face Rig=:> Mouth area [V]
+            #    2020/07/05 03:06:35 /dev/chr/Civilian_Child_2/asset.progress  Rigging=:> Ready to make AST [V]
+            #    2020/07/05 03:06:37 /dev/chr/Civilian_Child_2/Civilian_Child_2.blend [Openned]
+            #    2020/07/05 03:07:56 /dev/chr/Civilian_Child_2/autolink.data [Updated]
+            #    2020/07/05 03:08:08 pln/main.bos [Edited]
+            #    2020/07/05 03:08:13 /rnd/Scene_2a5jfq6126fe/1/Cc1_IK_Test.blend [Openned]
+            #    2020/07/05 03:08:25 /rnd/Scene_2a5jfq6126fe/1/Cc1_IK_Test.blend [Linked]
+            #    2020/07/05 03:08:25 /rnd/Scene_2a5jfq6126fe/1/Cc1_IK_Test.blend [Openned]
+            
+            # Also history has a feature missing in the schedules. But there is
+            # a rub. Scheduling you can rearange by moving them arround. And you
+            # can simply move a task to a different day if you so desire. 
+            
+            # In the history apart from the date you also have time up to the second
+            # which is a bit more precise in terms of when exactly you made a
+            # given task. And I would like scheduling to have the same type of
+            # precision. Maybe even introduce a notification system if a certain
+            # task is due. But it makes the moving of tasks highly complicated
+            # to visualize. 
+            
+            # Maybe a timeline could be a cool idea. Like in the VSE of blender.
+            # a place where you could put specific points on a timeline. And time
+            # to the next point will be hte length of the part. 
+            
+            # You could move the dots so to change timing of parts. And maybe
+            # select them to type in precise values. Could work.
+            
+            
+            # Let's separate the schedule to 3 types. Asset, Scene, Main. 
+            
+            # If it's a scene we are talking about. IK it's a bit shitty. Because
+            # I will need to parse this as well. But who cares.
+            
+            if f.startswith("/rnd"):
+                ty = "scenes"
+                url = f[:f.rfind("/")].replace("/rnd", "", 1)
+                url = url[:url.rfind("/")]
+                fn = f.replace("/rnd", "", 1).replace(url, "")
+                
+              
+            
+            
+            elif f.startswith("/dev"):
+                ty = "assets"
+                url = f[:f.rfind("/")].replace("/dev", "", 1)
+                fn = f.replace("/dev", "", 1).replace(url, "")
+                
+                
+                
+            else:
+                ty = "files"
+                url = ""
+                fn = f
+                
+            if not ty in data["dates"][d]:
+                data["dates"][d][ty] = {}
+            
+            if not url in data["dates"][d][ty]:
+                data["dates"][d][ty][url] = []
+            
+            data["dates"][d][ty][url].append([
+                "00:00:00",
+                "schedule",
+                fn,
+                t                    
+                ])
+    
+    # Okay I don't really know what exactly did I just do. But it's seems like
+    # a good starting point. Up to a working verion of VCStudio i can edit and
+    # change these all I want. Okay I guess it's time to parse another, way 
+    # harder file. The history. LOL.  
+    
+    # First let's deal with the persantage history file. It's kind of a funny
+    # one as well. The funny part is that it has no consistancy at all. I will
+    # need to convert it as well. It has a stupid %y-%m-%d format. Which is WTF.
+    
+    history_percentage_format = "%y-%m-%d"
+    
+    hdata = open(project_location+"/percentage_hystory.data") # IK spelling LOL
+    hdata = hdata.read()
+    hdata = hdata.split("\n")
+    
+    for date in hdata:
+        if date.startswith("DATE"):
+            
+            # An example of the formatting is actually quite amazing. It's going
+            # to be realively simple. Example:
+            
+            # DATE 20-06-26 42.64%            
+            
+            # As you can see it's the word DATE then that date then the fraction
+            # as a percantage. Separated by a spacebar.
+            
+            try:
+                t, d, f = date.split(" ")
+                
+                # Converting the d into a normal date
+                
+                d = datetime.datetime.strptime(d, history_percentage_format)
+                d = datetime.datetime.strftime(d, new_date_format)
+                
+                # Converting the f into a fraction
+                
+                f = float(f.replace("%", "")) / 100
+                
+            except:
+                continue
+            
+            # I just don't want to deal with poeple who are editing the file
+            # manually and then say I did something wrong that the program crashed
+            
+            if d not in data["dates"]:
+                data["dates"][d] = {}
+            
+            data["dates"][d]["fractions"] = {
+                "project":f,     # The fraction we recieved from the file
+                "checklist":0.0, # Place holder for checklist fraction
+                "chr":0.0,       #  \
+                "veh":0.0,       #  |
+                "loc":0.0,       #   > - Plaseholders for categories
+                "obj":0.0,       #  |
+                "rnd":0.0        #  /
+                }
+    
+    
+    # Okay this file was parsed. Which was relativelly simple. NOW. Let's
+    # do something a bit harder. Or is it as simple as the other? IDK
+    # History file. Example was a bit earlier. 
+    
+    hdata = open(project_location+"/history.data")
+    hdata = hdata.read()
+    hdata = hdata.split("\n")
+    
+    for line in hdata:
+        
+        if not line:
+            continue
+        
+        # So basically every line is quite simple from the left side. But becomes
+        # quite complex on the right side. So let's get the left side things
+        
+        date = line[:line.find(" ")]
+        time = line[line.find(" ")+1:line.replace(" ", ".", 1).find(" ")]
+        path = line[line.replace(" ", ".", 1).find(" ")+1:line.replace(" ", ".", 2).find(" ")]
+        done = line[line.replace(" ", ".", 2).find(" ")+1:]
+        
+        # I made a mistake allowing paths to have spaces in them. And I need to
+        # make sure we have the whole path and not only up to a point.
+        
+        while done[0] != "[" and ".progress" not in path and "[" in done:
+            
+            transporting = done[:done.find(" ")]
+            
+            path = path+" "+transporting
+            done = done.replace(transporting+" ", "")
+            
+        
+        # "date" will be our standard date. yyyy/mm/dd. Then "time" is out time
+        # from the current day. 24 hours system. hh:mm:ss. "path" is the file to
+        # which the change was done. We will need to parse this one. Because it
+        # contains the full path to let's say a blend file. While we want to 
+        # know which asset for example was accessed. Then "done" is the rest of
+        # the string. Basically it tells what was actually done with a given file.
+        # We will need to parse this one as well somehow. And it scares me a lot
+        
+        # One thing that we can use it for is restoring the old scheduling list.
+        # Becuase in lazy attempt to make user know that a given task is finished
+        # I would just delete the task from the schedule file entirely. But
+        # in the history file you can find instances of [scheduled] with the full
+        # path of a task and the date to which it was scheduled. So yeah.
+        
+        # Let's get to work.
+        
+        f = path # Copied from previous
+        
+        if f.startswith("/rnd"):
+            ty = "scenes"
+            url = f[:f.rfind("/")].replace("/rnd", "", 1)
+            url = url[:url.rfind("/")]
+            fn = f.replace("/rnd", "", 1).replace(url, "")
+            
+        
+        elif f.startswith("/dev"):
+            ty = "assets"
+            url = f[:f.rfind("/")].replace("/dev", "", 1)
+            fn = f.replace("/dev", "", 1).replace(url, "")
+        
+        elif f.startswith("/ast") and ".blend" in f:
+            ty = "assets"
+            url = f[:f.rfind(".")].replace("/ast", "", 1)
+            fn = "[asset_blend]"
+            
+        else:
+            ty = "files"
+            url = ""
+            fn = f
+        
+        # Now in order to parse the "done" variable. We need to define functions
+        # that we are not looking for. Simple (Trivial) Operations.
+        
+        simple_operations = [
+            "[Edited]",
+            "[Openned]",
+            "[Linked]",
+            "[Edited]",
+            "[Updated]",
+            "[Added]",
+            "[Added Asset]"            
+            ]
+        
+        # Basically if "done" in on that list. It's something to do with 
+        # checklists. And then we have either [V], [ ] or [Scheduled]
+        
+        if done not in simple_operations:
+            if "[Scheduled]" in done:
+                
+                # These are the missing scheduling data I was talking about.
+                # Let's parse this data from the back. The one on the back
+                # should be the DATE. Then spacebar. Then the [Scheduled] then
+                # another spacebar then the task. 
+                
+                missingdate = done[done.rfind(" ")+1:]
+                missingtask = done[:done.rfind(" [Scheduled]")].split("=:>")
+                
+                # Let's add them into the data. I don't what it will be. Let's
+                # see.
+                
+                
+                if not missingdate in data["dates"]:
+                    data["dates"][missingdate] = {}
+                
+                if not ty in data["dates"][missingdate]:
+                    data["dates"][missingdate][ty] = {}
+                
+                if not url in data["dates"][missingdate][ty]:
+                    data["dates"][missingdate][ty][url] = []
+                
+                data["dates"][missingdate][ty][url].append([
+                    "00:00:00",
+                    "schedule",
+                    fn,
+                    missingtask                    
+                    ])
+                
+            # Or else it's a checklist whether been checked or unchecked    
+                
+            else:
+                if "[V]" in done:
+                    done = done.replace(" [V]", "").split("=:>")
+                    check = True
+                else:
+                    done = done.replace(" [ ]", "").split("=:>")
+                    check = False
+                
+                # Putting the thing into the data
+                
+                if not date in data["dates"]:
+                    data["dates"][date] = {}
+                
+                if not ty in data["dates"][date]:
+                    data["dates"][date][ty] = {}
+                
+                if not url in data["dates"][date][ty]:
+                    data["dates"][date][ty][url] = []
+                
+                data["dates"][date][ty][url].append([
+                    time,
+                    "history",
+                    fn,
+                    "[Checklist]",
+                        [
+                        done,
+                        check
+                        ]                    
+                    ])
+        
+        # Now let's add all the others.
+        
+        else:
+            if not date in data["dates"]:
+                data["dates"][date] = {}
+            
+            if not ty in data["dates"][date]:
+                data["dates"][date][ty] = {}
+            
+            if not url in data["dates"][date][ty]:
+                data["dates"][date][ty][url] = []
+            
+            data["dates"][date][ty][url].append([
+                time,
+                "history",
+                fn,
+                done                    
+                ])
+                
+        #print(ty, url, fn, done)
+    
+    
+            
+            
+    data_save(data)  
     return data
 
+def data_save(data):
+    
+    # This function will save the data to the /set folder. It will include a
+    # a whole lot of inforamtion. Basically all analytics data will be there.
+    
+    new_date_format = "%Y/%m/%d"
+    
+    lines = []
+    
+    lines.append("##################### VCStudio Analytics File #####################")
+    lines.append("")
+    lines.append("    Last update: "+datetime.datetime.strftime(datetime.datetime.today(), new_date_format))
+    lines.append("")
+    lines.append("    Project : "+data["name"])
+    lines.append("    Director: "+data["director"])
+    lines.append("    Comment : "+data["status"])
+    lines.append("")
+    lines.append("    From: "+data["startdate"]+" To: "+data["deadline"])
+    lines.append("")
+    lines.append("-------------------------------------------------------------------")
+    lines.append("")
+    lines.append("    CURREND PROGRESS  : "+str(data["fraction"]*100)+"%")
+    lines.append("")
+    lines.append("    Current actuall   : "+str(data["donework"]*100)+"%")
+    lines.append("    Current checklist : "+str(data["checklist"]*100)+"%")
+    lines.append("")
+    lines.append("    Current Characters: "+str(data["chr"]*100)+"%")
+    lines.append("    Current Vehicled  : "+str(data["veh"]*100)+"%")
+    lines.append("    Current Locations : "+str(data["loc"]*100)+"%")
+    lines.append("    Current Other(obj): "+str(data["obj"]*100)+"%")
+    lines.append("    Current Scenes    : "+str(data["rnd"]*100)+"%")
+    lines.append("")
+    lines.append("-------------------------------------------------------------------")
+    lines.append("")
+    lines.append("    Bias:")
+    lines.append("        Characters: X "+str(data["chr_factor"]))
+    lines.append("        Vehicled  : X "+str(data["veh_factor"]))
+    lines.append("        Locations : X "+str(data["loc_factor"]))
+    lines.append("        Other(obj): X "+str(data["obj_factor"]))
+    lines.append("        Scenes    : X "+str(data["rnd_factor"]))
+    lines.append("")
+    lines.append("#####################    Schedule / History   #####################")
+    lines.append("")
+    
+    for date in sorted(data["dates"]):
+        lines.append("    "+date)
+        
+        date = data["dates"][date]
+
+        if "fractions" in date:
+            lines.append("")
+            lines.append("        Actuall   : "+str(date["fractions"]["project"]*100)+"%")
+            lines.append("")
+            lines.append("        Characters: "+str(date["fractions"]["chr"]*100)+"%")
+            lines.append("        Vehicles  : "+str(date["fractions"]["veh"]*100)+"%")
+            lines.append("        Locations : "+str(date["fractions"]["loc"]*100)+"%")
+            lines.append("        Other(obj): "+str(date["fractions"]["obj"]*100)+"%")
+            lines.append("        Scenes    : "+str(date["fractions"]["rnd"]*100)+"%")
+        
+        for thing in ["assets", "scenes", "files"]:
+            if thing in date:
+                
+                lines.append("")
+                lines.append("    ---- "+thing+" :")
+                
+                for asset in sorted( date[thing] ):
+                    lines.append("        ---- "+asset+" : ")
+                    lines.append("")
+                    for task in date[thing][asset]:
+                        
+                        
+                        if task[1] == "schedule":
+                            lines.append("             "+task[0]+" "+task[1])
+                            lines.append("                 "+task[2])
+                            lines.append("")
+                            spaces =     "                 "
+                            for directory in task[3]:
+                                spaces = spaces + ":..."
+                                lines.append(spaces+directory)
+                        
+                        elif task[3] == "[Checklist]":
+                            lines.append("             "+task[0]+" "+task[1]+" "+task[3])
+                            lines.append("                 "+task[2])
+                            lines.append("")
+                            spaces =     "                 "
+                            okay = " [Un-Checked]"
+                            if task[4][1]:
+                                okay = " [Checked]"
+                            for directory in task[4][0]:
+                                spaces = spaces + ":..."
+                                add_okay = ""
+                                if directory == task[4][0][-1]:
+                                    add_okay = okay
+                                lines.append(spaces+directory+add_okay)
+                        
+                        else:
+                            lines.append("             "+task[0]+" "+task[1]+" "+task[3])
+                            lines.append("                 "+task[2])
+                        
+                        lines.append("")
+                
+        
+        lines.append("")
+        lines.append("-------------------------------------------------------------------")
+        lines.append("")
+    
+    #test = open("/home/vcs/Desktop/testfile.vcsa", "w")
+    
+    
+    for i in lines:
+        print(i)
+        #test.write(i+"\n")
+    
+    #test.close()