Category: Coding & Scripting

PHP: get version details from composer.json

Let’s say we have a composer.json file as follows:

{
    "name": "mixable/blog",
    "description": "mixable.blog",
    "homepage": "https://mixable.blog",
    "version": "4.0.1",
    "type": "project",
    "require": {
        "php": "^8.x",
        "vendor/package": "^4.3",
        ...
    }
}

When using this file to manage the packages of an app, it might be necessary to check for the version of your app. This is possible by using the Composer class \Composer\InstalledVersions. The class provides different methods to access the details of the projects composer.json file.

Get details about the root package

Details about the root package are available through the getRootPackage() method:

$package = \Composer\InstalledVersions::getRootPackage();

This method returns an array with the following details:

name: string
version: string
reference: string
pretty_version: string
aliases: string[]
dev: bool
install_path: string
type: string

To get the apps version, you can use:

$version = \Composer\InstalledVersions::getRootPackage()['version'];

Get details about installed packages

There is a number of methods that provide additional information about the installed packages. Some examples:

Version of an installed package

$version = \Composer\InstalledVersions::getVersion('vendor/package');

Install path of an installed package

$installPath = \Composer\InstalledVersions::getInstallPath('vendor/package');

A detailed description of all methods is available at https://getcomposer.org/doc/07-runtime.md#installed-versions.

Photo by Ilya Pavlov on Unsplash

GitLab CI + Flutter: pub: command not found

In one of my projects, I used a GitLab environment to perform Flutter tests. For this, I setup my .gitlab-ci.yaml to use a Flutter docker image of cirrusci/flutter for code quality check or tests. The file looks like this:

code_quality:
  stage: test
  image: "cirrusci/flutter:stable"
  tags:
    - docker
  before_script:
    - pub global activate dart_code_metrics
    - export PATH="$PATH":"$HOME/.pub-cache/bin"
  script:
    - metrics lib -r codeclimate  > gl-code-quality-report.json
      
# [...]

test:
  stage: test
  image: "cirrusci/flutter:stable"
  tags:
    - docker
  before_script:
    - pub global activate junitreport
    - export PATH="$PATH":"$HOME/.pub-cache/bin"
  script:
    - flutter test --machine --coverage | tojunit -o report.xml
    - lcov --summary coverage/lcov.info
    - genhtml coverage/lcov.info --output=coverage

# [...]

Up to version 2.10.* of the Flutter docker image, this worked fine. But starting with version 3.0.0, there seems to be some changes in the binaries or their paths. The scripts failed with an error:

/usr/bin/bash: line 123: pub: command not found

To fix this error, the pub commands need to be adjusted and set to flutter pub:

# [...]

  before_script:
    - flutter pub global activate dart_code_metrics

# [...]

  before_script:
    - flutter pub global activate junitreport

# [...]

This fixed the issue and all tests finished successfully.

MySQL error: Cannot truncate a table referenced in a foreign key constraint

By default, you cannot TRUNCATE a table that has foreign key constraints applied on it. This is to keep the data consistent over multiple tables that are linked by constraints.

Solution 1

The fastest way to clean the table is to disable FOREIGN_KEY_CHECKS:

SET FOREIGN_KEY_CHECKS = 0; 
TRUNCATE `table1`;
TRUNCATE `table2`;
SET FOREIGN_KEY_CHECKS = 1;

Keep in mind, that disabling FOREIGN_KEY_CHECKS might result in inconsistent data with foreign key values that do not exists.

Solution 2

To remove all rows, you can also go the long way:

  • Remove all foreign key constraints
  • Run TRUNCATE on the table(s)
  • Manually delete all rows without reference
  • Recreate the foreign key constraints

Photo by Jan Antonin Kolar on Unsplash

Markdown: how do I add a newline in a table?

When creating tables in markdown, you use the vertical line | to split the columns. Each line of the document represents a row of the table. So adding a normal line break will result in a new row by default. Let’s assume you have a table with a large text and you want to split this text only in a single cell to improve readability. When using this markdown:

| Date | Action | Query |
| ---- | ------ | ----- |
| 2022-04-28 | none | search |
| 2022-04-25 | /url/to/my/page, added details about this topic, improved description | new |

This will create the following output:

To add a line break inside a table cell, you can simply use a html line break <br> . This is also suggested for line breaks in simple text outside tables. So the same contents with a line break:

| Date | Action | Query |
| ---- | ------ | ----- |
| 2022-04-28 | none | search |
| 2022-04-25 | /url/to/my/page<br> added details about this topic, improved description | new |

Will result in the following output:

And there you have it: a table with a line break inside a cell.

Why does the iOS App Store show more languages than my app supports?

By default, the languages of an app are listed in info.plist by setting the values for the properties list key CFBundleLocalizations. A definition as shown below should result in supported languages English and German.

	<key>CFBundleLocalizations</key>
	<array>
		<string>en</string>
		<string>de</string>
	</array>

But when looking at the AppStore, the languages shown there do not always correlated with this setting. The reason is that the languages shown on the AppStore are generated automatically based on the localized *.lproj folders found in your app.

Normally those folders should be in sync with your properties list setting. But when you use third party libraries (e.g. with CocoaPods) additional localizations might be loaded into your app. In this case, all *.lproj folders found in your app and in the pods are used for language determination.

How to correct the languages?

There is a plugin for that: cocoapods-prune-localizations, which can be simply added to your Podfile. When running pod install, this script will remove all localized files from pods or just keep the specified languages. To install the script, run:

gem install cocoapods-prune-localizations

Then add the following lines to your Podfile:

plugin 'cocoapods-prune-localizations'

Localizations will be inferred from your project.

or if you would prefer to specify the localizations:

plugin 'cocoapods-prune-localizations', {:localizations => ["en", "es"]}

This will keep the English and Spanish localizations in the Pods. Modify the localizations to your needs.

Photo by Etienne Girardet on Unsplash

Build and Release a Flutter App

Updating the app’s version number

To update the version number, navigate to the pubspec.yaml file and update the following line with the current version string:

version: 1.0.0+1

After the change, run:

flutter pub get

Build and release the iOS app

A detailled description of the whole process is described at docs.flutter.dev.

To release the iOS app, you use Flutter to build a xcarchive file. This build archive can be published the same way you would do it with Xcode by using the archive manager and one of the different Distribution options.

Build the iOS app:

flutter build ipa

The generated xcarchive file is saved to your app directory under:

/build/ios/archive/MyApp.xcarchive

Build and release the Android app

A detailled description of the whole process is described at docs.flutter.dev.

To release the Android app, you use Flutter to build a app bundle aab file. This file can be distributed by using Google Play Console or any other store.

Build the Android app:

flutter build appbundle

The generated aab app bundle file is saved to your app directory under:

/build/app/outputs/bundle/release/MyApp.aab

Photo by Artur Shamsutdinov on Unsplash

Ping server on a specific port

Terminal

You can’t ping ports, as Ping is using ICMP which doesn’t have the concept of ports. Ports belong to the transport layer protocols like TCP and UDP. However, you could use nmap to see whether ports are open or not:

nmap -p 80 example.com

The output will look like this:

nmap -p 80 google.de
Starting Nmap 7.92 ( https://nmap.org ) at 2021-12-08 10:59 CET
Nmap scan report for google.de (142.250.185.131)
Host is up (0.017s latency).
rDNS record for 142.250.185.131: fra16s50-in-f3.1e100.net

PORT   STATE SERVICE
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 0.09 seconds

Source: https://serverfault.com/questions/309357/ping-a-specific-port

Free and Easy Horizontal Stacked Bar Graphs with Google Colab / Jupyter (as well as Python and Pandas)

Recently, my wife needed to create some stacked bar graphs for her publications. She wasn’t able to find a tool on the internet which allowed her to do this in an easy and shareable way. So I used this opportunity to look into Jupyter Notebooks. These notebooks allow you to document and run code and thus computations on a remote server from your browser and display the results directly in your browser as well

Google offers their version of Jupyter as a free service called Colab. Already having a Google account it was an easy choice to start there. But there are other similar services available on the internet and the code should run there as well (with minimal changes).

The file is quite self explanatory and can be saved within your own Google drive for editing. You can find it here.

The Notebook

Horizontal Stacked Bar Graph

This notebook will create a horizontal stacked bar graph within your browser based on the data you enter below. Just edit the data in 1) and execute the “gray blocks” in 1), 2) and 3) by either pressing “Shift + Enter” or the “Play” button in each blocks top left corner. To see the “Play” button you need to move your mouse over the block.

You can change the design of the graph in 2). This includes the unit which should be displayed on the bar. You will moste likely need to change the position of the legend as it depends on the data and size of the graph. To do this, change the x value of FigLegPosOffset slightly.

The code within a block is written in Python. Everything after a # is considered a comment. So you can add your own comments to your data or your changes.

1) Preparing the Data
Define your data below and execute the block when done. If everything is correct, your data will be displayed as table below the block.

# importing the functions needed to generate the graph 
import pandas as pd

# Axis captions
FigAxiXCap="Cocktails"
FigAxiYCap="Which Cocktail do you want for the Party?"

# Data (beware: the first entry will be displayed last)

## Data Indexes 
DataIndexes = ['Caipirinha', 'Piña Colada', 'Cuba Libre', 'Mai Tai', 'Ipanema', 'Caribbean Night']
## Data Options
DataYesPlease =  [73.2, 23.8, 24.7, 31.2, 56.6, 68.2]
DataSureWhyNot = [20.3, 51.7, 60.3, 19.2,  4.8, 22.3]
DataJustNo =     [ 6.5, 24.5,   15, 59.6, 38.6,  9.5]

## Name of each column (for the legend)
df = pd.DataFrame(data={'Yes, please!': DataYesPlease, 'Sure, why not?': DataSureWhyNot, 'Just no!': DataJustNo})

#-------------------------------------------------------------------------------
#no need to edit below this line
df.index = DataIndexes

# print table for convinience/debugging (transposed)
df

2) Figure Properties

In the block below you can change the format of the figure. When done (or fine with the defaults) execute it to set all properties.

#################
# Figure Design #
#################
#Figure
## Figure Size
FigSizeX = 20
FigSizeY = 5
## Figure background color 
FigBackColor="white"
## Axis Caption Size
FigAxiXCapFontSize=45
FigAxiYCapFontSize=45
## Axis Labels Font Size
FigLabelFontSize=20

# Blocks
## Colormap of the blocks (all Options: https://matplotlib.org/stable/gallery/color/colormap_reference.html)
FigColMap="Pastel2"
## Width of bars (everything above 1 will overlap)
FigBarsWidth=0.8
## FontSize of bar lables
FigBarsFontSize=20
# Data and Captions (needs to be defined)
## Unit to display within bars
FigBarUnit="%"

# Legend
## Legend Font Size
FigLegFontSize=20
## Legend position [best|upper right|upper left|lower left|lower right|right|center left|center right|lower center|upper center|center]
FigLegPos="upper center"
## Figure OffSet (will move the legend box slightly to match actual figure position)
FigLegPosOffset=(1.11, 1.0)

## 3) Generate and Display the Figure
The block below will generate the figure. You do not need to change anything here. Just be sure to execute it again **after changing anything above**.

# define plot type and layout
ax = df.plot(stacked=True, kind='barh', figsize=(FigSizeX, FigSizeY), colormap=FigColMap, width=FigBarsWidth, fontsize=FigBarsFontSize, xlim=[0,100])

# Add labels to the bars 
# Attribution: Trenton McKinney on StackExchange: https://stackoverflow.com/a/60895640/3764407 CC BY-SA 4.0
for rect in ax.patches:
    # Find where everything is located
    height = rect.get_height()
    width = rect.get_width()
    x = rect.get_x()
    y = rect.get_y()
    
    # The width of the bar is the data value and can be used as the label
    label_text = ""
    if width > 4:
      label_text = f'{width:.1f}' + FigBarUnit  ##### use {width:.2f} or {width:.3f} to increase precision  ####
    
    # ax.text(x, y, text)
    label_x = x + width / 2
    label_y = y + height / 2

    # plot only when height is greater than specified value
    if height > 0:
        ax.text(label_x, label_y, label_text, ha='center', va='center', fontsize=FigBarsFontSize)

# Set legend position and style
ax.legend(bbox_to_anchor=FigLegPosOffset, loc=FigLegPos, borderaxespad=0., fontsize=FigLegFontSize)    
# Set y label
ax.set_ylabel(FigAxiXCap, fontsize=FigAxiXCapFontSize)
# Set x label
ax.set_xlabel(FigAxiYCap, fontsize=FigAxiYCapFontSize)
# Set Background Color
ax.set_facecolor(FigBackColor)

3) Generate and Display the Figure
The block below will generate the figure. You do not need to change anything here. Just be sure to execute it again after changing anything above.

define plot type and layout
ax = df.plot(stacked=True, kind='barh', figsize=(FigSizeX, FigSizeY), colormap=FigColMap, width=FigBarsWidth, fontsize=FigBarsFontSize, xlim=[0,100])

# Add labels to the bars 
# Attribution: Trenton McKinney on StackExchange: https://stackoverflow.com/a/60895640/3764407 CC BY-SA 4.0
for rect in ax.patches:
    # Find where everything is located
    height = rect.get_height()
    width = rect.get_width()
    x = rect.get_x()
    y = rect.get_y()
    
    # The width of the bar is the data value and can be used as the label
    label_text = ""
    if width > 4:
      label_text = f'{width:.1f}' + FigBarUnit  ##### use {width:.2f} or {width:.3f} to increase precision  ####
    
    # ax.text(x, y, text)
    label_x = x + width / 2
    label_y = y + height / 2

    # plot only when height is greater than specified value
    if height > 0:
        ax.text(label_x, label_y, label_text, ha='center', va='center', fontsize=FigBarsFontSize)

# Set legend position and style
ax.legend(bbox_to_anchor=FigLegPosOffset, loc=FigLegPos, borderaxespad=0., fontsize=FigLegFontSize)    
# Set y label
ax.set_ylabel(FigAxiXCap, fontsize=FigAxiXCapFontSize)
# Set x label
ax.set_xlabel(FigAxiYCap, fontsize=FigAxiYCapFontSize)
# Set Background Color
ax.set_facecolor(FigBackColor)

4) Download the Figure (from Google Colab)
To export the generated figure use the following block. You can change the file name and type as well as its resolution (dots per inch/DPI).

# Define the file name
FigureFileName="figure"
# Define the type (jpg, png, pdf, svg)
FigureFileType="png"
# Define DPI (setting this too high might result in long wait times and finally an error)
FigureDPI=30

# do not edit below
FigFile=FigureFileName + "." + FigureFileType
ax.figure.savefig(FigFile,dpi=FigureDPI,bbox_inches = 'tight')
from google.colab import files
files.download("/content/" + FigFile )