Belle II Software development
ValidationRoot Class Reference

Public Member Functions

def __init__ (self, working_folder, gitlab_object, gitlab_config, gitlab_map)
 
def create_comparison (self)
 
def index (self)
 
def plots (self, *args)
 
def check_comparison_status (self)
 
def revisions (self, revision_label=None)
 
def comparisons (self, comparison_label=None)
 
def system_info (self)
 
def retrieve_file_metadata (self, filename)
 
def create_issue (self, title, description)
 
def issue (self, file_path, rev_label, contact)
 
def issue_redirect (self, iid)
 
def update_issue (self, id, file_path, rev_label)
 

Public Attributes

 working_folder
 html folder that contains plots etc.
 
 last_restart
 Date when this object was instantiated.
 
 version
 Git version.
 
 gitlab_object
 Gitlab object.
 
 gitlab_config
 Gitlab config.
 
 gitlab_map
 Gitlab usermap.
 
 file_path
 placeholder variable for path
 
 revision_label
 placeholder variable for revision label
 
 contact
 placeholder variable for contact
 

Detailed Description

Root Validation class to handle non-static HTTP requests into the
validation server. The two main functions are to hand out compiled json
objects of revisions and comparisons and to start and monitor the
creation of comparison plots.

Definition at line 651 of file validationserver.py.

Constructor & Destructor Documentation

◆ __init__()

def __init__ (   self,
  working_folder,
  gitlab_object,
  gitlab_config,
  gitlab_map 
)
class initializer, which takes the path to the folders containing the
validation run results and plots (aka comparison), gitlab object and
config

Definition at line 661 of file validationserver.py.

661 def __init__(self, working_folder, gitlab_object, gitlab_config, gitlab_map):
662 """
663 class initializer, which takes the path to the folders containing the
664 validation run results and plots (aka comparison), gitlab object and
665 config
666 """
667
668
669 self.working_folder = working_folder
670
671
672 self.last_restart = datetime.datetime.now()
673
674
676 os.environ["BELLE2_LOCAL_DIR"]
677 )
678
679
680 self.gitlab_object = gitlab_object
681
682
683 self.gitlab_config = gitlab_config
684
685
686 self.gitlab_map = gitlab_map
687
688
689 self.file_path = None
690
691 self.revision_label = None
692
693 self.contact = None
694
Optional[str] get_compact_git_hash(str repo_folder)

Member Function Documentation

◆ check_comparison_status()

def check_comparison_status (   self)
Checks on the status of a comparison creation

Definition at line 744 of file validationserver.py.

744 def check_comparison_status(self):
745 """
746 Checks on the status of a comparison creation
747 """
748 progress_key = cherrypy.request.json["input"]
749 logging.debug("Checking status for plot creation: " + str(progress_key))
750 status = check_plotting_status(progress_key)
751 return status
752

◆ comparisons()

def comparisons (   self,
  comparison_label = None 
)
return the json file of the comparison results of one specific
comparison

Definition at line 859 of file validationserver.py.

859 def comparisons(self, comparison_label=None):
860 """
861 return the json file of the comparison results of one specific
862 comparison
863 """
864
865 warn_wrong_directory()
866
867 # todo: Make this independent of our working directory!
868 path = os.path.join(
869 os.path.relpath(
871 self.working_folder, comparison_label.split(",")
872 ),
873 validationpath.get_html_folder(self.working_folder),
874 ),
875 "comparison.json",
876 )
877
878 # check if this comparison actually exists
879 if not os.path.isfile(path):
880 raise cherrypy.HTTPError(
881 404, f"Json Comparison file {path} does not exist"
882 )
883
884 return deliver_json(path)
885
def get_html_folder(output_base_dir)
def get_html_plots_tag_comparison_folder(output_base_dir, tags)

◆ create_comparison()

def create_comparison (   self)
Triggers the start of a now comparison between the revisions supplied
in revision_list

Definition at line 698 of file validationserver.py.

698 def create_comparison(self):
699 """
700 Triggers the start of a now comparison between the revisions supplied
701 in revision_list
702 """
703 rev_list = cherrypy.request.json["revision_list"]
704 logging.debug("Creating plots for revisions: " + str(rev_list))
705 progress_key = start_plotting_request(
706 rev_list,
707 validationpath.get_results_folder(self.working_folder),
708 )
709 return {"progress_key": progress_key}
710
def get_results_folder(output_base_dir)

◆ create_issue()

def create_issue (   self,
  title,
  description 
)
Call the functions to create the issue and redirect
to the created Gitlab issue page.

Definition at line 920 of file validationserver.py.

920 def create_issue(self, title, description):
921 """
922 Call the functions to create the issue and redirect
923 to the created Gitlab issue page.
924 """
925
926 # check if this is a plot/script based on file format
927 issue_type = 'plot'
928 if 'log' == self.file_path.split("/")[-1].split(".")[-1]:
929 issue_type = 'script'
930 default_section = self.gitlab_config['global']['default']
931 project_id = self.gitlab_config[default_section]['project_id']
932 # Create issue in the Gitlab project and save it
933 project = get_project_object(self.gitlab_object, project_id)
934 uploaded_file = upload_file_gitlab(self.file_path, project)
935 assignees = {
936 'gitlab_ids': [],
937 'usernames': [],
938 }
939 file_name = self.file_path.split("/")[-1].split(".log")[0]
940 file_package = self.file_path.split("/")[-2]
941 if self.gitlab_map:
942 assignees = parse_contact(
943 self.contact, self.gitlab_map, file_package, self.gitlab_object
944 )
945 description += "\n\n---\n\n:robot: Automated code, please do not delete\n\n" + \
946 f"Relevant {issue_type}: {file_name}\n\n" + \
947 f"Revision label: {self.revision_label}\n\n---"
948 issue_id = create_gitlab_issue(
949 title, description, uploaded_file, assignees, file_package, project
950 )
951 project.save()
952
953 # Update JSON with created issue id - script and plot info reside
954 # in different locations and also have different structures.
955 # todo - maybe this can be combined?
956 if issue_type == 'script':
957 revision_json_path = os.path.join(
958 validationpath.get_results_folder(self.working_folder),
959 self.revision_label,
960 validationpath.file_name_results_json,
961 )
962 update_scriptfile_issues_json(
963 revision_json_path, file_name, file_package, issue_id)
964 else:
965 comparison_json_path = os.path.join(
966 validationpath.get_html_plots_folder(self.working_folder),
967 self.file_path.split("/")[-3],
968 "comparison.json",
969 )
970 update_plot_issues_json(
971 comparison_json_path, file_name, file_package, issue_id)
972
973 issue_url = self.gitlab_config[default_section]['project_url'] \
974 + "/-/issues/" \
975 + str(issue_id)
976 raise cherrypy.HTTPRedirect(
977 issue_url
978 )
979
def get_html_plots_folder(output_base_dir)

◆ index()

def index (   self)
forward to the static landing page if
the default url is used (like http://localhost:8080/)

Definition at line 712 of file validationserver.py.

712 def index(self):
713 """
714 forward to the static landing page if
715 the default url is used (like http://localhost:8080/)
716 """
717 raise cherrypy.HTTPRedirect("/static/validation.html")
718

◆ issue()

def issue (   self,
  file_path,
  rev_label,
  contact 
)
Return a template issue creation interface
for the user to add title and description.

Definition at line 981 of file validationserver.py.

981 def issue(self, file_path, rev_label, contact):
982 """
983 Return a template issue creation interface
984 for the user to add title and description.
985 """
986 self.file_path = os.path.join(
987 validationpath.get_html_folder(self.working_folder), file_path
988 )
989
990 self.revision_label = rev_label
991 self.contact = contact
992
993 if not self.gitlab_object:
994 return "ERROR: Gitlab integration not set up, verify config file."
995
996 raise cherrypy.HTTPRedirect("/static/validation_issue.html")
997

◆ issue_redirect()

def issue_redirect (   self,
  iid 
)
Redirect to the Gitlab issue page.

Definition at line 999 of file validationserver.py.

999 def issue_redirect(self, iid):
1000 """
1001 Redirect to the Gitlab issue page.
1002 """
1003 default_section = self.gitlab_config['global']['default']
1004 if not self.gitlab_config[default_section]['project_url']:
1005 return "ERROR: Gitlab integration not set up, verify config file."
1006
1007 issue_url = self.gitlab_config[default_section]['project_url'] \
1008 + "/-/issues/" \
1009 + str(iid)
1010 raise cherrypy.HTTPRedirect(
1011 issue_url
1012 )
1013

◆ plots()

def plots (   self,
args 
)
Serve file from the html/plot directory.
:param args: For the request /plots/a/b/c, these will be the strings
    "a", "b", "c"

Definition at line 720 of file validationserver.py.

720 def plots(self, *args):
721 """
722 Serve file from the html/plot directory.
723 :param args: For the request /plots/a/b/c, these will be the strings
724 "a", "b", "c"
725 """
726
727 warn_wrong_directory()
728
729 if len(args) < 3:
730 raise cherrypy.HTTPError(404)
731
732 tag_folder = os.path.relpath(
734 self.working_folder, args[:-2]
735 ),
736 validationpath.get_html_folder(self.working_folder),
737 )
738 path = os.path.join(tag_folder, *args[-2:])
739 return cherrypy.lib.static.serve_file(path)
740

◆ retrieve_file_metadata()

def retrieve_file_metadata (   self,
  filename 
)
Returns:
    Metadata(str) of the file

Definition at line 908 of file validationserver.py.

908 def retrieve_file_metadata(self, filename):
909 """
910 Returns:
911 Metadata(str) of the file
912 """
913 cherrypy.response.headers['Content-Type'] = 'text/plain'
914 metadata = validationfunctions.get_file_metadata(filename)
915 return metadata
916
str get_file_metadata(str filename)

◆ revisions()

def revisions (   self,
  revision_label = None 
)
Return a combined json object with all revisions and
mark the newest one with the field most_recent=true

Definition at line 755 of file validationserver.py.

755 def revisions(self, revision_label=None):
756 """
757 Return a combined json object with all revisions and
758 mark the newest one with the field most_recent=true
759 """
760
761 # get list of available revision
762 rev_list = get_json_object_list(
763 validationpath.get_results_folder(self.working_folder),
764 validationpath.file_name_results_json,
765 )
766
767 # always add the reference revision
768 combined_list = []
769 reference_revision = json.loads(
770 json_objects.dumps(json_objects.Revision(label="reference"))
771 )
772
773 # load and combine
774 for r in rev_list:
775 full_path = os.path.join(
776 validationpath.get_results_folder(self.working_folder),
777 r,
778 validationpath.file_name_results_json,
779 )
780
781 # update label, if dir has been moved
782 lbl_folder = get_revision_label_from_json_filename(full_path)
783 j = deliver_json(full_path)
784 j["label"] = lbl_folder
785 combined_list.append(j)
786
787 # Sorting
788
789 # Order by categories (nightly, release, etc.) first, then by date
790 # A pure chronological order doesn't make sense, because we do not
791 # have a linear history ((pre)releases branch off) and for the builds
792 # the date corresponds to the build date, not to the date of the
793 # actual commit.
794 def sort_key(label: str):
795 if "-" not in label:
796 logging.warning(
797 f"Misformatted label encountered: '{label}' "
798 f"(doesn't seem to include date?)"
799 )
800 return label
801 category, datetag = label.split("-", maxsplit=1)
802 print(category, datetag)
803 # Will later reverse order to bring items in the same category
804 # in reverse chronological order, so the following list will have
805 # the items in reverse order as well:
806 order = ["release", "prerelease", "nightly"]
807 try:
808 index = order.index(category)
809 except ValueError:
810 index = 9
811 logging.warning(
812 f"Misformatted label encountered: '{label}' (doesn't seem "
813 f"to belong to any known category?)"
814 )
815 return f"{index}-{datetag}"
816
817 combined_list.sort(key=lambda rev: sort_key(rev["label"]), reverse=True)
818
819 # reference always on top
820 combined_list = [reference_revision] + combined_list
821
822 # Set the most recent one ...
823 newest_date = None
824 newest_rev = None
825 for r in combined_list:
826 rdate_str = r["creation_date"]
827 if isinstance(rdate_str, str):
828 if len(rdate_str) > 0:
829 try:
830 rdate = time.strptime(rdate_str, "%Y-%m-%d %H:%M")
831 except ValueError:
832 # some old validation results might still contain
833 # seconds and therefore cannot properly be converted
834 rdate = None
835
836 if rdate is None:
837 continue
838
839 if newest_date is None:
840 newest_date = rdate
841 newest_rev = r
842 if rdate > newest_date:
843 newest_date = rdate
844 newest_rev = r
845
846 for c in combined_list:
847 if c["most_recent"] is not None:
848 c["most_recent"] = False
849
850 # if there are no revisions at all, this might also be just None
851 if newest_rev:
852 newest_rev["most_recent"] = True
853
854 # topmost item must be dictionary for the ractive.os template to match
855 return {"revisions": combined_list}
856
def dumps(obj)

◆ system_info()

def system_info (   self)
Returns:
    JSON file containing git versions and time of last restart

Definition at line 888 of file validationserver.py.

888 def system_info(self):
889 """
890 Returns:
891 JSON file containing git versions and time of last restart
892 """
893
894 warn_wrong_directory()
895
896 # note: for some reason %Z doesn't work like this, so we use
897 # time.tzname for the time zone.
898 return {
899 "last_restart": self.last_restart.strftime("%-d %b %H:%M ")
900 + time.tzname[1],
901 "version_restart": self.version,
903 os.environ["BELLE2_LOCAL_DIR"]
904 ),
905 }
906

◆ update_issue()

def update_issue (   self,
  id,
  file_path,
  rev_label 
)
Update existing issue in Gitlab with current result plot
and redirect to the updated Gitlab issue page.

Definition at line 1015 of file validationserver.py.

1015 def update_issue(self, id, file_path, rev_label):
1016 """
1017 Update existing issue in Gitlab with current result plot
1018 and redirect to the updated Gitlab issue page.
1019 """
1020
1021 if not self.gitlab_object:
1022 return "ERROR: Gitlab integration not set up, verify config file."
1023
1024 plot_path = os.path.join(
1025 validationpath.get_html_folder(self.working_folder), file_path
1026 )
1027
1028 default_section = self.gitlab_config['global']['default']
1029 project_id = self.gitlab_config[default_section]['project_id']
1030 project = get_project_object(self.gitlab_object, project_id)
1031 uploaded_file = upload_file_gitlab(plot_path, project)
1032 update_gitlab_issue(
1033 id, uploaded_file, project, plot_path, rev_label
1034 )
1035 project.save()
1036
1037 issue_url = self.gitlab_config[default_section]['project_url'] \
1038 + "/-/issues/" \
1039 + str(id)
1040
1041 raise cherrypy.HTTPRedirect(
1042 issue_url
1043 )
1044
1045

Member Data Documentation

◆ contact

contact

placeholder variable for contact

Definition at line 693 of file validationserver.py.

◆ file_path

file_path

placeholder variable for path

Definition at line 689 of file validationserver.py.

◆ gitlab_config

gitlab_config

Gitlab config.

Definition at line 683 of file validationserver.py.

◆ gitlab_map

gitlab_map

Gitlab usermap.

Definition at line 686 of file validationserver.py.

◆ gitlab_object

gitlab_object

Gitlab object.

Definition at line 680 of file validationserver.py.

◆ last_restart

last_restart

Date when this object was instantiated.

Definition at line 672 of file validationserver.py.

◆ revision_label

revision_label

placeholder variable for revision label

Definition at line 691 of file validationserver.py.

◆ version

version

Git version.

Definition at line 675 of file validationserver.py.

◆ working_folder

working_folder

html folder that contains plots etc.

Definition at line 669 of file validationserver.py.


The documentation for this class was generated from the following file: