portapack-mayhem/firmware/tools/generate_ui-about-simple.cpp.py

221 lines
7.1 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# Copysomething (c) 2024 LupusE with the license, needed by the PortaPack project
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
import argparse
import json
import os
import re
import requests
import sys
cppheader = """#include "ui_about_simple.hpp"
#include <string_view>
#define ROLL_SPEED_FRAME_PER_LINE 60
// cuz frame rate of pp screen is probably 60, scroll per sec
namespace ui {
// Information: a line starting with a '#' will be yellow coloured
constexpr std::"""
cppfooter = """
AboutView::AboutView(NavigationView& nav) {
add_children({&menu_view,
&button_ok});
button_ok.on_select = [&nav](Button&) {
nav.pop();
};
menu_view.on_left = [this]() {
button_ok.focus();
};
menu_view.on_right = [this]() {
button_ok.focus();
};
for (auto& authors_line : authors_list) {
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
if (authors_line.size() > 0) {
if (authors_line[0] == '#') {
menu_view.add_item(
{(std::string)authors_line.substr(1, authors_line.size() - 1),
ui::Theme::getInstance()->fg_yellow->foreground,
nullptr,
nullptr});
} else {
menu_view.add_item(
{(std::string)authors_line,
Theme::getInstance()->bg_darkest->foreground,
nullptr,
nullptr});
}
}
}
}
void AboutView::on_frame_sync() {
if (interacted) return;
if (frame_sync_count++ % ROLL_SPEED_FRAME_PER_LINE == 0) {
const auto current = menu_view.highlighted_index();
const auto count = menu_view.item_count();
if (current < count - 1) {
menu_view.set_highlighted(current + 1);
} else {
menu_view.set_highlighted(0);
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
menu_view.set_highlighted(2); // the first line that has human name
}
}
}
void AboutView::focus() {
button_ok.focus();
menu_view.set_highlighted(2); // the first line that has human name
}
bool AboutView::on_touch(const TouchEvent) {
interacted = true;
return false;
}
bool AboutView::on_key(const KeyEvent) {
interacted = true;
return false;
}
bool AboutView::on_encoder(const EncoderEvent) {
interacted = true;
menu_view.focus();
return false;
}
} /* namespace ui */
"""
def get_contributors(url):
contributors_list = [] ## Raw list of contributor names
logins = json.loads(requests.get(url).text)
try:
print(f"Could not reach URL - Error: {logins['status']} Messsage: {logins['message']}")
## Status f.ex. 404 / message f.ex. "Not Found" or "API rate limit exceeded for...".
sys.exit()
except:
pass
for gh_user in logins:
clean_ghuser = re.sub('[^a-zA-Z0-9-]', '.', gh_user['login']) ## replace non printable character with .
contributors_list.append(clean_ghuser)
contributor_id = 0
contrib_format = [] ## Formated list of contributors, max 30 char per line
for contributor in contributors_list:
contributor_id +=1
contrib_format.append(contributor)
line_len = len(",".join(contrib_format).split("\n")[-1])
if contributor_id < len(contributors_list) and line_len+len(contributors_list[contributor_id]) >=30:
contrib_format.append("\n")
return(",".join(contrib_format).replace("\n,","\n")) # replace to erase first , in followup lines
def generate_content(projects):
project_contrib = []
project_contrib.append("string_view authors_list[] = {\n")
project_contrib.append(" \"# * List of contributors * \",\n")
for project in projects:
project_contrib.append(" \" \",\n")
project_contrib.append(f" \"#{project[0]}:\",\n")
url = f"https://api.github.com/repos/{project[1]}/{project[2]}/contributors?per_page={project[3]}"
contrib_mayhem = get_contributors(url).split("\n")
for line in contrib_mayhem:
project_contrib.append(f" \"{line}\",\n")
project_contrib.append(" \" \"};\n")
return("".join(project_contrib))
def pp_create_ui_about_simple_cpp(cpp_file, cppheader, cppcontent, cppfooter):
uiaboutsimplecpp_file = []
uiaboutsimplecpp_file.append(cppheader)
uiaboutsimplecpp_file.append(cppcontent)
uiaboutsimplecpp_file.append(cppfooter)
with open(cpp_file, "w", encoding="utf-8") as file:
file.writelines(uiaboutsimplecpp_file)
print("Find your ui_simple_about.cpp at", cpp_file)
def pp_change_ui_about_simple_cpp(cpp_file, cppcontent):
content = []
content_pattern = re.compile(r"string_view authors_list\[\] = {\n(?:\s+(?:.*,\n)+\s+.*};\n)", re.MULTILINE)
# Read original file
with open(cpp_file, 'r') as file:
filedata = file.read()
# Replace regex content with generated list
for match in content_pattern.finditer(filedata):
content.append(match[0])
filedata = filedata.replace(content[0], cppcontent)
# Write new file
with open(cpp_file, 'w') as file:
file.write(filedata)
projects = []
## Format: Project title, Github name, Github repo, Amount of contributors
projects.append(["Mayhem-Firmware","portapack-mayhem","mayhem-firmware","50"])
projects.append(["Havoc","furrtek","portapack-havoc","50"])
projects.append(["PortaPack","sharebrained","portapack-hackrf","50"])
projects.append(["HackRF","mossmann","hackrf","15"])
## processing
#############
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="This tool updates the ui_about_simple.cpp from PortaPack firmware from Github contributors. If no file is given, the file will be generated.",
)
parser.add_argument("--cpp", help="Path of the source ui_about_simple.cpp to update.", default="ui_about_simple.cpp")
parser.add_argument("--new","-n", help="Generate a new file")
args = parser.parse_args()
if args.cpp:
cpp_file = os.path.join(os.getcwd(),args.cpp)
else:
cpp_file = os.path.join(os.getcwd(),"ui_about_simple.cpp")
cppcontent = generate_content(projects)
if args.new or not os.path.isfile(args.cpp):
pp_create_ui_about_simple_cpp(cpp_file, cppheader, cppcontent, cppfooter)
else:
pp_change_ui_about_simple_cpp(cpp_file, cppcontent)