Helpdesk: VSC – zwiększenie wcięć w explorerze

2024-10-22

Nie wiem, czy to już wzrok zawodzi, czy co, ale jak dla mnie okienko explorera w VSC jest jakoś takie nieczytelne. Foldery są mało widoczne, zlewają sie z plikami itp. Ale da sie to zmienić!

W ustawieniach (File >> Preferences >> Settings) VSC odszukaj ustawienia „tree indent”. Ja ustwiłem sobie tą wartość na „zabójcze” 32, ale 16 też znacznie poprawia widoczność wcięć:

By Rafał Kraik in Helpdesk

Helpdesk: migracja Windows 11 na nowy dysk (klonowanie dysku)

2024-10-19

Gdy w końcu dojrzejesz do decyzji o wymianie dysku na większy, nie chcesz przechodzić przez proces instalacji systemu operacyjnego, instalacji oprogramowania, konfiguracji itp. itd. W takim przypadku pomocne może być klonowanie dysku.

Istnieje dużo aplikacji, które pozwolą sklonować dysk, problem tylko w tym, jak sobie te programy poradzą zwłaszcza z utrzymaniem możliwości bootowania ze sklonowanego dysku. Dlatego ku pamięci opiszę tu z jakich narzędzi korzystałem, a jakie zawiodły i dlaczego.

Mój plan nr 1 to skorzystanie z oprogramowania dodawanego do dysku. Zdecydowałem się na dysk Western Digital Black Edition, a WD dodaje do niego licencję na Acronis True Image. Program ten ma zadziałać z całą mocą jeśli tylko wykryje dysk WD podpięty do systemu. Rozpakowałem i przy pomocy obudowy Natec Rino podłączyłem dysk do komputera. Ze strony WD pobrałem Acronis-a. Zainstalowałem, a potem otworzyłem program i… zobaczyłem komunikat o błędzie aktywacji licencji. Oprogramowanie nie widziało dysku WD i nie chciało się aktywować. Rzeczywiście dysk identyfikował się w systemie jako Jmicron i stało się tak raczej nie dlatego, że sprzedawca sprzedał podróbkę, ale po prostu WD przejęło firmę Jmicron. Wypełniłem więc zgłoszenie usterki i machnąłem ręką na WD i Acronisa. Szkoda, bo większość dysków, jakie mam to WD i jak do tej pory nie miałem z nimi większych problemów, a tu taka klapa. Gdyby jednak wszystko zadziałało, pewnie kontynuowałbym tak jak na tym video albo tak, jak opisali to w instrukcji.

Co ciekawe, już we wspomnianej instrukcji pisano, że nowy dysk musi być zainstalowany w komputerze, a stary może się znajdować „na zewnątrz”. Podobno połączenie dysków odwrotnie (stary w komputerze, nowy w obudowie na zewnątrz), może spowodować, że nowy dysk po klonowaniu nie będzie sie bootował. Tymczasem…. znalazłem lepsze rozwiązanie, które takiego problemu nie miało.

Bohaterami zostali:

Clonezille można pobrać w formacie ISO, u mnie plik nazywał się clonezilla-live-3.2.0-5-amd64. Ten obraz ISO należy następnie przegrać np. na pendrive, ale w taki sposób, aby dysk USB był bootowalny i właśnie w tym przegraniu obrazu pomaga Rufus.

Tak więc:

  • pobrałem Clonezille
  • pobrałem Rufusa
  • znalazłem pen drive
  • włożyłem pen drive do portu USB
  • uruchomiłem Rufusa i przeniosłem obraz ISO clonezilli na pendrive

Dokładny opis, jak to zrobić znajduje się np. tutaj: https://beitadmin.pl/jak-sklonowac-dysk-za-pomoca-clonezilli/

Następnie dokładnie tak, jak jest to opisane we wspomnianym artykule

  • podłączyłem nowy dysk do komputera za pomocą zewnętrznej obudowy
  • uruchomiłem komputer z pendrive
  • krok po kroku powiedzialem Clonezilli, co chcę zrobić. Trzeba tylko uważać na wybierane opcje. Przy drobnym błędzie można sobie sklonować nowy (pusty) dysk na stary (z twoimi cennymi danymi)
  • potem wyłączyłem komputer i wymieniłem dyski miejscami. Teraz nowy dysk był już w komputerze
  • następnie trzymając kciuki włączyłem komputer i WSZYSTKO DZIAŁALO

Może jedyna wada tego rozwiązania była taka, że na nowym dysku powstał układ systemu plików i podział partycji, taki sam ja na oryginalnym dysku. W moim przypadku migracja była z dysku 500GB na dysk 2TB, więc po prostu utworzyłem dodatkowy wolumen przeznaczając na niego to dodatkowe wolne miejsce. Może tak i jest lepiej?

Podsumowując: zawiodłem się na bonusach rozdawanych przez WD w postaci programu Acronis True Image. Nadal uważam, że dyski WD są dobrej jakości. Znowu przekonałem się, że open source i rozwiązania oparte o Linuxa prezentują świetną jakość. Polecam Clonezille i Rufusa!

By Rafał Kraik in Helpdesk

PowerShell: grep

2024-10-16

Oj, brakuje pod Windows prostej funkcji grep, ale skoro jest PowerShell, to można sobie taką funkcję zrobić samemu!

Get-ChildItem c:\temp\*.* -Recurse -File | Select-String -Pattern "TODO:"

By Rafał Kraik in Power Shell, SQL

GitHub: Pull Request review w GitHub CLI

2024-10-15

GitHub jest super, ale kiedy chcesz zautomatyzować co nieco w zakresie pracy z kodem, to przyda się praca z linii komend. Jeśli jeszcze nie masz zainstalowanego GitHub CLI to pobierz je.

Pełna lista komend gh znajduje się tutaj: GitHub CLI | Take GitHub to the command line

Zaczynamy w cmd od przejścia do właściwego repo. To ważne, bo chociaż komendy gh pozwalają przy pomocy parametru wskazać, na którym repo będą wykonywane pewne czynności, to wygodnie jest korzystać z domyślnego repo, którym jest to, gdzie uruchomiło się gh

Pracę zaczyna się od zalogowania.

gh auth login

Teraz zależnie od konfiguracji mogą wydarzyć się różne rzeczy, ale np. autoryzować można się za pomocą Personal Access Token (PAT). Wystarczy wkleić PAT i już zostaniemy uwierzytelnieni jako właściwy użytkownik z właściwym dostępem.

Aby wyświetlić oczekujące Pull Request uruchom

gh pr list

W odpowiedzi wyświetli się lista oczekujących pull requestów. Można podejrzeć metadane uruchamiając polecenie view z odpowiednim numerem pr:

gh pr view 2

Żeby zobaczyć jakie zmiany idą za pull request uruchom

gh pr diff 2

Jednym z zadań do wykonania podczas akceptacji pull request jest wykonanie review, a w tym review często dodajemy komentarz (opcja c odpowiada za komentarz, a opcja b za blok tekstu):

gh pr review 2 -c -b „good idea!”

Oprócz review można też zaakceptować request:

gh pr review 2 –approve

No i wreszcie pull request można dołączyć do brancha:

gh pr merge 2

Do kompletu zostałoby jeszcze tylko może w jaki sposób utworyć nowy pull request. Zrobisz to tak:

gh pr create –base main –head „automation”–title „Automation added” –body „Added automation to data processing”

By Rafał Kraik in Git

Hyper-V: Konfiguracja sieci „internal” z dostępem do Internetu

2024-10-08

Hyper-V pozwala utworzyć Virtual Switch typu:

  • private – wyizolowana sieć, bez dostępu do sieci hosta
  • internal – wewnętrzna wirtualna sieć, do której dostęp może mieć komputer wirtualny, jak i system hosta, co za tym idzie, można udostępnić Internet
  • external – maszyna wirtualna „widzi” tę samą sieć, co host

Jeśli chcesz, aby maszyna wirtualna miała dostęp do Internetu, to przez eliminację odpada sieć private. Można by skorzystać z sieci external, ale wtedy jest delikatny problem z adresem IP komputera wirtualnego, bo jest on przyznawany przez DHCP, a możesz preferować jednak stały adres IP. Ewentualne skonfigurowanie statycznego adresu IP na maszynie wirtualnej może doprowadzić do konfliktów, a tego byśmy nie chcieli. I tak zostaje sieć internal.

No to po kolei:

  • Otwórz managera Hyper-V i w Virtual Switch Manager utwórz sieć typu internal. Możemy ją nazwać np. InternalSwitch. Można to też zrobić poleceniem PowerShell:
New-VMSwitch -SwitchName "InternalSwitch" -SwitchType Internal
  • Na komputerze hosta w PowerShell uruchomionym jako administrator uruchom kolejno polecenia, które skonfigurują tę maszynę do NAT:
    • Przypisanie do interfejsu hosta odpowiadającego za komunikację z siecią wewnętrzną stałego adresu IP (tutaj 192.168.0.1). Jeśli nie lubisz PowerShella, można to też wyklikać
New-NetNAT -Name "InternalNAT" -InternalIPInterfaceAddressPrefix "192.168.0.0/24"
  • Nadal w Powershell na hoście
    • Konfiguracja wewnętrznej sieci do NAT. Tutaj trzeba użyć PowerShella, lub ewentualnie dodać do hosta rolę RRAS (Routing and Remote Access Server), ale to dość dużo pracy w porównaniu do jednej komendy
New-NetIPAddress -IPAddress 192.168.0.1 -PrefixLength 24 -InterfaceAlias "vEthernet (InternalSwitch)"
  • W ustawieniach maszyny wirtualnej podłącz interfejs sieciowy do sieci internal
  • Skonfiguruj maszynę wirtualną ze stałym adresem IP:
    • Adres IP – dowolny nieużywany jeszcze adres IP z zakresu 192.168.0.0/24
    • Brama domyślna – 192.168.0.1 – czyli ten sam adres IP, co używany na wewnętrznym interfejsie hosta
    • DNS – dowolny, np googlowski 8.8.8.8

U mnie działa!

By Rafał Kraik in Helpdesk

Linux: Kilka połączeń na jednym interfejsie

2024-10-08

Network manager to sprytna bestia. Można dla jednego interfejsu sieciowego zdefiniowac np. polaczenie net-static, ktore przypisuje temu interfejsowi adres statyczny  oraz polaczenie net-dynamic, ktore przydziela interfejsowi adres z DHCP. W danej chwili może być aktywne tylko jedno połącznie, ale w ramach potrzeb można się przełączyć z jednej konfiguracji do drugiej. Zwykle jedno połączenie odpowiada jednemu interfejsowi, ale… warto wiedzieć, że się da 🙂

Tak można by zdefiniować net-static:

nmcli connection add type ethernet con-name net-static ifname eth0 ip4 192.168.1.100/24 gw4 192.168.1.1

a tak można zdefiniować net-dynamic:

nmcli connection add type ethernet con-name net-dynamic ifname eth0

Potem można aktywować net-static:

nmcli connection up net-static

A tak można aktywować net-dynamic:

nmcli connection up net-dynamic

Żeby zobaczyć listę dostępnych połączeń dla jednego interfejsu użyj:

nmcli connection show --active

By Rafał Kraik in Linuxy

Azure: MS Fabric i Spark Notebooks

2024-10-03

Apache Spark (spark) pracuje na obiektach data frame podobnych do tych z Pandas, ale zoptymalizowanych do pracy z Spark engine. Tutaj wczytujemy (read) plik csv (format), który w pierwszym wierszu ma nagłówek (option) z pliku (load):

df = spark.read.format("csv").option("header","true").load("Files/orders/2019.csv")
# df now is a Spark DataFrame containing CSV data from "Files/orders/2019.csv".
display(df)

Plik CSV może nie mieć nagłówka i wtedy po jego wczytaniu kolumny będą miały nazwy _c01, _c02, … Dlatego można „nałożyć” na plik CSV strukturę. W takim przypadku będziemy pracować z czymś, co przypomina tabelę SQL. Rzeczywiście w spark mamy definicje typów SQL. Tutaj wczytujemy bibliotekę pyspark.sql.types, a następnie tworzymy strukturę (StructType), która składa się z kolumn (StructField), a te kolumny mogą być różnego typu, np.: StringType, IntegerType, DateType, FloatType. Zmienia się też sposób wczytania danych. Nadal czytamy (read) dane w postaci csv (format), ale teraz nadajemy im strukturę (schema), a same dane pobieramy z pliku (load).

from pyspark.sql.types import *

orderSchema = StructType([
    StructField("SalesOrderNumber", StringType()),
    StructField("SalesOrderLineNumber", IntegerType()),
    StructField("OrderDate", DateType()),
    StructField("CustomerName", StringType()),
    StructField("Email", StringType()),
    StructField("Item", StringType()),
    StructField("Quantity", IntegerType()),
    StructField("UnitPrice", FloatType()),
    StructField("Tax", FloatType())
    ])

df = spark.read.format("csv").schema(orderSchema).load("Files/orders/2019.csv")
display(df)

Jeśli masz kilka plików CSV o takiej samej strukturze, to możesz je załadować jednocześnie zamieniając nazwę pliku na maskę:

df = spark.read.format("csv").schema(orderSchema).load("Files/orders/*.csv")

W oparciu o jeden data frame można tworzyć kolejne. Można np. wybrać tylko niektóre kolumny wymieniając je w nawiasach kwadratowych. Data frame ma automatycznie kilka przydatnych funkcji. Np. count() policzy ile wierszy ma data frame, distinct() zwróci tylko wiersze unikalne, a co za tym idzie distinct().count() policzy ile wierszy jest unikalnych:

customers = df['CustomerName', 'Email']
print(customers.count())
print(customers.distinct().count())
display(customers.distinct())

Zamiast wymieniania nazw kolumn w nawiasie kwadratowym, można też użyć jawnej funkcji select:

customers = df.select("CustomerName", "Email")

Data frame ma metodę where, która pozwala odfiltrować wiersze spełniające określone warunki. Warunek definiuje się odwołując się do kolumn danych. Np. tutaj filtr wskazuje, że należy wyświetlić te wiersze, które w kolumnie Item mają określony tekst:

customers = df.select("CustomerName", "Email").where(df['Item']=='Road-250 Red, 52')
print(customers.count())
print(customers.distinct().count())
display(customers.distinct())

Dane z data frame można grupować. Wystarczy wskazać, które kolumny zawierają dane „po których” ma się odbywać grupowanie, oraz wybrać funkcję agregującą:

productSales = df.select("Item", "Quantity").groupBy("Item").sum()

Spark może się posługiwać funkcjami występującymi w SQL. Te funkcje znajdują się w module pyspark.sql.functions. Dzięki temu można uruchomić funkcję, np. tutaj year, przekazać do niej kolumnę (col), oraz nadać jej alias (alias). Dodatkowo dane można posortować (orderBy):

from pyspark.sql.functions import *yearlySales = df.select(year(col("OrderDate")).alias("Year")).groupBy("Year").count().orderBy("Year")display(yearlySales)

Podczas transformacji można wyznaczać nowe kolumny, których wartość jest zbudowana w oparciu o istniejące kolumny. W tym celu korzysta się z withColumn, której argumentem jest nazwa nowo tworzonej kolumny oraz wyrażenie wyznaczające wartość tej kolumny. W wyrażeniach można używać funkcji takich, jak: year, month, split, getItem. Kolumny można eliminować lub zmieniać ich kolejność korzystając z metody select lub z nawiasów kwadratowych, w których zostaną wymienione we właściwej kolejności tylko te kolumny, które mają pozostać.

from pyspark.sql.functions import *

transformed_df = df.withColumn("Year", year(col("OrderDate"))).withColumn("Month", month(col("OrderDate")))

transformed_df = transformed_df.withColumn("FirstName", split(col("CustomerName"), " ").getItem(0)).withColumn("LastName", split(col("CustomerName"), " ").getItem(1))

transformed_df = transformed_df["SalesOrderNumber", "SalesOrderLineNumber", "OrderDate", "Year", "Month", "FirstName", "LastName", "Email", "Item", "Quantity", "UnitPrice", "Tax"]

display(transformed_df.limit(5))

Przetworzone dane można „zwrócić” zapisując je (write) do pliku. Można zdecydować, że plik ma być nadpisany (mode), oraz wskazać na format tworzonego pliku (parquet).

transformed_df.write.mode("overwrite").parquet('Files/transformed_data/orders')
print ("Transformed data saved!")

Dane zapisane w formacie parquet można wczytać, wskazując na format i lokalizację. Słowo order w ścieżce oznacza nazwę katalogu. W tym katalogu w formacie parquet są przechowywane dane.

orders_df = spark.read.format("parquet").load("Files/transformed_data/orders")
display(orders_df)

Podczas zapisu danych, można je partycjonować. Tutaj we wskazanym w metodzie parquet katalogu powstaną podkatalogi Year i Month wskazujące na konkretną partycję, np. Year=2022/Month=1:

orders_df.write.partitionBy("Year","Month").mode("overwrite").parquet("Files/partitioned_data")
print ("Transformed data saved!")

Następnie w intuicyjny sposób można wczytać tylko część danych:

orders_2021_df = spark.read.format("parquet").load("Files/partitioned_data/Year=2021/Month=*")
display(orders_2021_df)

Jeśli preferujesz pracować z danymi z wykorzystaniem SQL, to możesz zapisać dane „jako tabela” (saveAsTable). Opisanie danych strukturą SQL może być wykonane w wewnętrznym metastore Sparka (tzw managed table) lub może odnosić się do pliku, który znajduje się w data lake (external table). W tej metodzie nie podajemy pełnej ścieżki dostępu do pliku, co oznacza, że będzie on przechowywany w metastore. „delta” to jeden z dostępnych formatów, inne to: csv, parquet, avro itd. Te formaty mogą wprowadzać typowo bazo-danowe funkcjonalności jak transakcje, wersjonowanie rekordów itp.

df.write.format("delta").saveAsTable("salesorders")spark.sql("DESCRIBE EXTENDED salesorders").show(truncate=False)

Mając tak zdefiniowaną tabelę, można pisać do niej zapytania SQL i zapisywać wynik w data frame spark:

df = spark.sql("SELECT * FROM xyz_lakehouse.salesorders LIMIT 1000")
display(df)

Ponieważ spark pozwala na pracę z różnymi językami, to można przełączyć się do języka SQL komendą %%sql

%%sql
SELECT YEAR(OrderDate) AS OrderYear,
       SUM((UnitPrice * Quantity) + Tax) AS GrossRevenue
       FROM salesorders       
       GROUP BY YEAR(OrderDate)
       ORDER BY OrderYear;

Przełączając się między widokami, można dokonać wstępnej analizy danych na automatycznie generowanych wykresach:

Z drugiej strony Spark to nadal notebook, w którym jest uruchamiany kod Python, dlatego można korzystać z licznych funkcjonalności, które występują w tym języku, np. można tworzyć wykresy z wykorzystaniem popularnego modułu matplotlib:

sqlQuery = "SELECT CAST(YEAR(OrderDate) AS CHAR(4)) AS OrderYear, \
                SUM((UnitPrice * Quantity) + Tax) AS GrossRevenue \
                FROM salesorders \
                GROUP BY CAST(YEAR(OrderDate) AS CHAR(4)) \
               ORDER BY OrderYear"
df_spark = spark.sql(sqlQuery)
df_spark.show()

from matplotlib import pyplot as pl

tdf_sales = df_spark.toPandas()
plt.bar(x=df_sales['OrderYear'], height=df_sales['GrossRevenue'])
plt.show()

Mając pythonowe funkcje możemy wpływać na to, jak taki wykres ma wyglądać:

from matplotlib import pyplot as plt

plt.clf()
plt.bar(x=df_sales['OrderYear'], height=df_sales['GrossRevenue'], color='orange')
plt.title('Revenue by Year')
plt.xlabel('Year')
plt.ylabel('Revenue')
plt.grid(color='#95a5a6', linestyle='--', linewidth=2, axis='y', alpha=0.7)
plt.xticks(rotation=45)
plt.show()

Matplotlib ma dużo możliwości pod względem formatowania wykresów i generalnie można korzystać ze wszystkich z nich. Tu np. tworzymy dwa wykresy w jednym:

from matplotlib import pyplot as plt

plt.clf()
fig, ax = plt.subplots(1, 2, figsize = (10,4))

ax[0].bar(x=df_sales['OrderYear'], height=df_sales['GrossRevenue'], color='orange')
ax[0].set_title('Revenue by Year')

yearly_counts = df_sales['OrderYear'].value_counts()

ax[1].pie(yearly_counts)
ax[1].set_title('Orders per Year')
ax[1].legend(yearly_counts.keys().tolist())
fig.suptitle('Sales Data')

plt.show()

Matplotlib jest uniwersalny, ale też pracochłonny. Równolegle powstają też inne biblioteki do tworzenia wykresów, jak np. seaborn:

import seaborn as sns

plt.clf()
ax = sns.barplot(x="OrderYear", y="GrossRevenue", data=df_sales)
plt.show()

Seaborn pozwala na zastosowanie „tematów” definiujących z grubsza wygląd wykresu:

import seaborn as sns

plt.clf()
sns.set_theme(style="whitegrid")
ax = sns.barplot(x="OrderYear", y="GrossRevenue", data=df_sales)
plt.show()

Do dyspozycji mamy wiele typów wykresów, w tym również liniowy:

import seaborn as sns

plt.clf()
ax = sns.lineplot(x="OrderYear", y="GrossRevenue", data=df_sales)
plt.show()
By Rafał Kraik in SQL