Commit 3fa18ae4 authored by Frede Hundewadt's avatar Frede Hundewadt

initial commit

parents
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
end_of_line = lf
[*.bat]
indent_style = tab
end_of_line = crlf
[LICENSE]
insert_final_newline = false
[Makefile]
indent_style = tab
*.py[cod]
# C extensions
*.so
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
sdist
develop-eggs
.installed.cfg
lib
lib64
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
htmlcov
mock/etc/pacman.d/mirrorlist
mock/usr/share/application-utility/mirrors.json
mock/var/lib/application-utility/status.json
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
.idea
.vscode
# Complexity
output/*.html
output/*/index.html
# Sphinx
docs/_build
pacman_mirrors/__pycache__/
tests/__pycache__/
# Credits
- fhdk <echo ZmhAbWFuamFyby5vcmcK | base64 -d>
- papajoke
Thank you all! ;)
# Change Log
All notable changes to this project will be documented in this file.
# Contributing
Contributions are welcome, and they are greatly appreciated! Every
little bit helps, and credit will always be given.
You can contribute in many ways:
## Types of Contributions
### Report Bugs
Report bugs at [Manjaro Gitlab](https://gitlab.manjaro.org/fhdk/).
If you are reporting a bug, please include:
- Your operating system name and version.
- Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
### Fix Bugs
Look through the Gitlab issues for bugs. Anything tagged with "bug"
is open to whoever wants to implement it.
### Implement Features
Look through the Gitlab issues for features. Anything tagged with "feature"
is open to whoever wants to implement it.
### Write Documentation
could always use more documentation, whether as part of the
official docs, in docstrings, or even on the web in blog posts,
articles, and such.
### Translations
Help us to ship in your language by help translating.
### Submit Feedback
The best way to send feedback is to file an issue at [Manjaro Gitlab](https://gitlab.manjaro.org/fhdk//issues).
If you are proposing a feature:
* Explain in detail how it would work.
* Keep the scope as narrow as possible, to make it easier to implement.
* Remember that this is a volunteer-driven project, and that contributions
are welcome :)
## Get Started!
Ready to contribute? Here's how to set up `` for local development.
* Fork the `` repo on GitHub.
* Clone your fork locally:
```
$ git clone https://gitlab.manjaro.org/your-name-here/.git
```
* Install your local copy into a virtualenv. Assuming you have [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/) installed, this is how you set up your fork for local development
* Remember to `source /usr/bin/virtualenvwrapper.sh` to get the virtualenv CLI
```
$ mkvirtualenv
$ cd /
$ python setup.py develop
```
* Create a branch for local development:
```
$ git checkout -b name-of-your-bugfix-or-feature
```
Now you can make your changes locally.
* When you're done making changes, commit your changes and push your branch to gitlab:
```
$ git add
$ git commit -m "Your detailed description of your changes."
$ git push origin name-of-your-bugfix-or-feature
```
* Submit a pull request through the Gitlab website.
## Pull Request Guidelines
Before you submit a pull request, check that it meets these guidelines:
* The pull request should include tests.
* If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.md.
## Tips
## Developing environment
* An editor of choice e.g.
* Visual Studio Code `yay -S visual-studio-code`
* PyCharm Community `pacman -Syu pycharm-community`
* Pandoc converter `pacman -Syu pandoc`
* Python environment
```
$ git clone https://gitlab.manjaro.org/fhdk/.git
$ cd
$ sudo pacman -Syu python-pip python-virtualenvwrapper
$ mkvirtualenv
$ python setup.py develop
$ pip install mkdocs coverage babel flake8 npyscreen transifex-client
```
This diff is collapsed.
include AUTHORS.md
include CONTRIBUTING.md
include CHANGELOG.md
include LICENSE
include README.md
include man/.8
include share/*.json
include scripts/manjaro-
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-include docs *.md
.PHONY: clean-pyc clean-build docs clean
help:
@echo "clean - remove all build, test, coverage and Python artifacts"
@echo "clean-build - remove build artifacts"
@echo "clean-pyc - remove Python file artifacts"
@echo "clean-test - remove test and coverage artifacts"
@echo "lint - check style with flake8"
@echo "test - run tests quickly with the default Python"
@echo "coverage - check code coverage quickly with the default Python"
@echo "docs - generate MkDocs HTML documentation, man page using Pandoc, including API docs"
@echo "release - package and upload a release"
@echo "dist - package"
@echo "install - install the package to the active Python's site-packages"
@echo "pot-file - extract messages to locale/application_utility.pot"
@echo "push-pot - push pot file to transifex"
@echo "pull-po - pull all translations from transifex"
@echo "mo-files - generate .mo files"
clean: clean-build clean-pyc clean-test
clean-build:
rm -fr build/
rm -fr dist/
rm -fr .eggs/
find . -name '*.egg-info' -exec rm -fr {} +
find . -name '*.egg' -exec rm -f {} +
clean-pyc:
find . -name '*.pyc' -exec rm -f {} +
find . -name '*.pyo' -exec rm -f {} +
find . -name '*~' -exec rm -f {} +
find . -name '__pycache__' -exec rm -fr {} +
clean-test:
rm -f .coverage
rm -fr htmlcov/
lint:
flake8 application_utility
test:
python setup.py test
coverage:
coverage run --source application_utility setup.py test
coverage report -m
coverage html
firefox htmlcov/index.html
docs:
mkdocs build
pandoc -s -t man docs/index.md -o man/.8
pandoc docs/index.md -f markdown -t html -s -o man/.8.html
gzip man/.8 -fq
man-page:
pandoc -s -t man docs/index.md -o man/.8
man man/.8
release: clean
python setup.py sdist upload
python setup.py bdist_wheel upload
dist: clean
python setup.py sdist
python setup.py bdist_wheel
ls -l dist
install: clean mo-files
python setup.py install --root=$(DESTDIR) --optimize=1
pot-file:
python setup.py extract_messages --output-file locale/application_utility.pot
push-pot:
tx push -s
pull-po:
tx pull -a
mo-files:
python setup.py compile_catalog --directory locale --domain application_utility
#
Package that provides a simple application utility for Manjaro Linux.
- Free software: GPL license
## Features
- A GUI for selecting common applications for installation or removal.
## Technologies
is build with Python and Gtk3.
#!/usr/bin/env python
import gi
import requests
from requests.exceptions import ConnectionError
import os
import logging
import sys
from .browser.app_config import AppConfig
from .browser.applications import Applications
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject, GdkPixbuf
# print(dir(gi.repository.Gtk))
# cat /usr/lib/python3.7/site-packages/gi/overrides/Gtk.py
# doc api
# https://lazka.github.io/pgi-docs/Gtk-3.0/index.html
# class UtilityConfig(AppConfig):
# """
# for Alone application
# not in this file ! ;)
# """
#
# def __init__(self):
# super().__init__(application="application-utility")
# self.url = None
# self.file = None
#
# def load(self):
# """ load file or url by parameter console"""
# self.preferences = {"data_set":"default" }
# self.url = {"desktop": "", "main": ""}
# self.file = {"desktop": "", "main": "share/default.json"}
# if len(sys.argv) > 1: # and not "--dev" in sys.argv:
# file = sys.argv[1]
# if os.path.isfile(file):
# self.file["main"] = file
# else:
# if file.startswith("http"):
# try:
# # TODO make only head() ?
# requests.get(file)
# self.url["main"] = file
# logging.info(f"json to use: [{self.url['main']}")
# # TODO save in self._JSONMERGED
# except ConnectionError:
# logging.critical('json data not found')
# # else:
# # self.file["main"] = file
# return self
class MainApp:
def __init__(self):
"""main app window"""
window = Gtk.Window(title='Manjaro Application Utility', border_width=6)
window.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
window.connect('delete-event', Gtk.main_quit)
window.connect('destroy', self.on_main_window_destroy)
window.set_default_size(800, 650)
"""
TODO ?
icon="system-software-install"
pixbuf24 = Gtk.IconTheme.get_default().load_icon(icon, 24, 0)
pixbuf32 = Gtk.IconTheme.get_default().load_icon(icon, 32, 0)
pixbuf48 = Gtk.IconTheme.get_default().load_icon(icon, 48, 0)
pixbuf64 = Gtk.IconTheme.get_default().load_icon(icon, 64, 0)
pixbuf96 = Gtk.IconTheme.get_default().load_icon(icon, 96, 0)
self.set_icon_list([pixbuf24, pixbuf32, pixbuf48, pixbuf64, pixbuf96])
"""
self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
window.add(self.main_box)
conf = AppConfig(application="application-utility")
self.app_box = Applications(conf, window)
self.main_box.pack_start(self.app_box, True, True, 0)
self.main_box.show_all()
window.show_all()
@staticmethod
def on_main_window_destroy(widget):
Gtk.main_quit()
def run(self):
logging.basicConfig(level=logging.DEBUG)
MainApp()
Gtk.main()
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
MainApp()
Gtk.main()
__all__ = ["alpm",
"app_config",
"applications",
"base_config",
"config",
"data",
"exceptions",
"hello_config",
"iso_config"]
"""
Module alpm
Install / remove packages
use pamac-installer
"""
import subprocess
import glob
import logging
class Alpm:
"""system alpm commit"""
NOTHING = 0
REMOVE = 1
ADD = 2
BOTH = 3
def __init__(self):
"""constructor"""
self.pkg_list_install = []
self.pkg_list_removal = []
def do_update(self):
"""run pamac"""
result = Alpm.NOTHING
if not self.pkg_list_install and not self.pkg_list_removal:
return result
if self.pkg_list_removal:
refresh = self._install_apps(self.pkg_list_removal, False)
if not refresh:
logging.warning("warning: packages not uninstalled")
else:
result = Alpm.REMOVE
if self.pkg_list_install:
refresh = self._install_apps(self.pkg_list_install, True)
if not refresh:
logging.warning("warning: packages not installed")
else:
if result == Alpm.NOTHING:
result = Alpm.ADD
else:
result = Alpm.BOTH
return result
def set_package(self, package_name: str, install: bool, installed: bool) -> None:
"""
Add or Remove package in lists
install: add or remove
"""
if self.to_remove(package_name):
self.pkg_list_removal.remove(package_name)
else:
if not install and installed:
self.pkg_list_removal.append(package_name)
if self.to_install(package_name):
self.pkg_list_install.remove(package_name)
else:
if install and not installed:
self.pkg_list_install.append(package_name)
def clear(self) -> None:
"""clear lists packages"""
self.pkg_list_install = []
self.pkg_list_removal = []
def to_install(self, package_name) -> bool:
"""package is in list ?"""
return package_name in self.pkg_list_install
def to_remove(self, package_name) -> bool:
"""package is in list ?"""
return package_name in self.pkg_list_removal
@classmethod
def _install_apps(cls, pkg_list: list, install: bool = True) -> bool:
"""
install or remove packages list
test presence first package in pacman DB
"""
if not pkg_list:
return False
if not install:
install = ['--remove']
else:
install = []
try:
subprocess.run(['pamac-installer'] + install + pkg_list, capture_output=True, check=True)
except FileNotFoundError:
# next with pamac-qt its in all isos
logging.warning('ERROR: Pamac not installed !')
raise
except subprocess.CalledProcessError as e:
# e.returncode > 0
logging.warning(f"ERROR in alpm commit: {e.returncode} ?\n{e.stderr}")
# after test if "cancel" btn
if not install:
return cls.app_installed(pkg_list[0])
return not cls.app_installed(pkg_list[0])
@property
def empty(self):
"""2 lists are empty ?"""
return not self.pkg_list_removal and not self.pkg_list_install
@staticmethod
def app_installed(package: str) -> bool:
"""test if package is installed"""
if glob.glob(f"/var/lib/pacman/local/{package}-[0-9]*"):
return True
return False
def __str__(self):
return f"pkg list install: {self.pkg_list_install}\n pkg list removal: {self.pkg_list_removal}"
#!/usr/bin/env python3
import logging
import os
import requests
import sys
from .config import Config
class AppConfig(Config):
"""
for Stand-alone application
"""
def load(self):
""" load file or url by parameter console"""
self.preferences = self.read_json_file(self._PREFERENCES.format(self.application))
self.url = {"desktop": "", "main": ""}
self.file = {"desktop": "", "main": "/usr/share/{}/{}.json".format(self.application,
self.preferences['data_set'])}
if self.dev:
self.file = {"desktop": "", "main": "share/{}.json".format(self.preferences['data_set'])}
if len(sys.argv) > 1:
file = sys.argv[1]
if os.path.isfile(file):
self.file["main"] = file
else:
try:
requests.get(file)
# TODO save in self._JSON_MERGED
except ConnectionError:
logging.critical('json data not found')
else:
self.url["main"] = file
return self
This diff is collapsed.
import collections
import json
import sys
class BaseConfig:
"""
set config from env or plugin or standalone App
pass this class to object Applications constructor())
"""
_PREFERENCES = r"/usr/share/{}/preferences.json"
_JSON_MERGED = r"/tmp/browser-preferences.json"
def __init__(self, application: str):
self.application = application
self.preferences = {}
self.url = {"desktop": "", "main": ""}
self.file = {"desktop": "", "main": ""}
self.dev = "--dev" in sys.argv
def load(self):
"""to override live iso ? desktop ?"""
raise NotImplementedError
# @property
# def filter(self) ->str:
# return self.apps.filter
#
# @filter.setter
# def filter(self, value: str) ->None:
# self.apps.filter = value
@staticmethod
def read_json_file(filename, dictionary=True):
"""
Read json data from file
"""
result = list()
try:
if dictionary:
with open(filename, "rb") as infile:
result = json.loads(
infile.read().decode("utf8"),
object_pairs_hook=collections.OrderedDict)
else:
with open(filename, "r") as infile:
result = json.load(infile)
except OSError:
pass
return result
import logging
import os
import shutil
import urllib.request
from .data import Data
from .base_config import BaseConfig
class Config(BaseConfig, Data):
"""Configuration with data"""
def load(self):
pass
def __init__(self, application: str):
BaseConfig.__init__(self, application)
Data.__init__(self)
self.load()
self.merge_json()
def merge_json(self):
"""
TODO
merge desktop in main in self.json ? and merge url in file ?
and use only self._JSON_MERGED ?
Always save .json in self._JSON_MERGED ?
"""
# write temp
if self.url["main"]:
src = "/tmp/{self.application}-download.json"
urllib.request.urlretrieve(self.url["main"], src)
logging.info(f"URL: [{self.url['main']} downloaded")
else:
src = self.file["main"]
logging.info(f"json to merge : {src}")
if not os.path.isfile(src):
logging.error(f"ERROR: File not found: {src}")
raise ImportError(src)
shutil.copyfile(src, self._JSON_MERGED)
logging.debug(f"json : {self._JSON_MERGED}")
self.load_from_file(self._JSON_MERGED)
import collections
import glob
import json
import logging
import os
from typing import Iterator
import gi
gi.require_version('Pamac', '1.0')
from gi.repository import Pamac
class Data:
def __init__(self):
self.filename = ""
self._json = []
self.filter = ""
self.group = "All"
self.desktop = os.environ.get("XDG_SESSION_DESKTOP", "?").lower()
def load_from_file(self, filename: str = ''):
"""
load json file
:param filename:
"""
if filename:
self.filename = filename
self._json = self._read_json_file(filename, True)
# self.__dict__.update(self.json) ??
# ValueError: dictionary update sequence element #0 has length 4; 2 is required
# self.all()
@property
def json(self) -> list:
"""
return json but with filters:
- desktop : self.desktop
- advanced 0/1 : self.filter
"""
logging.debug("\n--- read datas.json with FILTERS ---\n")
logging.debug(f"my desktop: {self.desktop}")
logging